├── .gitignore ├── LICENSE ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── generated └── resources │ ├── assets │ └── tutorialv3 │ │ ├── blockstates │ │ ├── generator.json │ │ ├── mysterious_ore_deepslate.json │ │ ├── mysterious_ore_end.json │ │ ├── mysterious_ore_nether.json │ │ ├── mysterious_ore_overworld.json │ │ ├── portal.json │ │ └── powergen.json │ │ ├── lang │ │ └── en_us.json │ │ └── models │ │ ├── block │ │ ├── generator.json │ │ ├── mysterious_ore_deepslate.json │ │ ├── mysterious_ore_end.json │ │ ├── mysterious_ore_nether.json │ │ ├── mysterious_ore_overworld.json │ │ ├── portal.json │ │ └── powergen │ │ │ ├── main.json │ │ │ ├── singleoff.json │ │ │ └── singleon.json │ │ └── item │ │ ├── generator.json │ │ ├── mysterious_ingot.json │ │ ├── mysterious_ore_deepslate.json │ │ ├── mysterious_ore_end.json │ │ ├── mysterious_ore_nether.json │ │ ├── mysterious_ore_overworld.json │ │ ├── portal.json │ │ ├── powergen.json │ │ ├── raw_mysterious_chunk.json │ │ └── thief.json │ └── data │ ├── forge │ └── tags │ │ ├── blocks │ │ └── ores.json │ │ └── items │ │ ├── ingots.json │ │ └── ores.json │ ├── minecraft │ ├── advancements │ │ └── recipes │ │ │ └── tutorialv3 │ │ │ ├── mysterious_ingot1.json │ │ │ └── mysterious_ingot2.json │ ├── recipes │ │ ├── mysterious_ingot1.json │ │ └── mysterious_ingot2.json │ └── tags │ │ └── blocks │ │ ├── mineable │ │ └── pickaxe.json │ │ └── needs_iron_tool.json │ └── tutorialv3 │ ├── advancements │ └── recipes │ │ └── tutorialv3 │ │ ├── generator.json │ │ └── powergen.json │ ├── loot_tables │ └── blocks │ │ ├── generator.json │ │ ├── mysterious_ore_deepslate.json │ │ ├── mysterious_ore_end.json │ │ ├── mysterious_ore_nether.json │ │ ├── mysterious_ore_overworld.json │ │ └── powergen.json │ ├── recipes │ ├── generator.json │ └── powergen.json │ └── tags │ ├── blocks │ └── mysterious_ore.json │ ├── items │ └── mysterious_ore.json │ └── worldgen │ ├── biome │ └── has_structure │ │ ├── portal.json │ │ └── thiefden.json │ └── structure_set │ └── mysterious_dimension_structure_set.json └── main ├── java └── com │ └── example │ └── tutorialv3 │ ├── TutorialV3.java │ ├── blocks │ ├── GeneratorBE.java │ ├── GeneratorBlock.java │ ├── GeneratorConfig.java │ ├── PortalBlock.java │ ├── PowergenBE.java │ ├── PowergenBlock.java │ ├── PowergenConfig.java │ └── PowergenContainer.java │ ├── client │ ├── CustomRenderType.java │ ├── GeneratorBakedModel.java │ ├── GeneratorModelLoader.java │ ├── ModelKey.java │ ├── PowergenRenderer.java │ └── PowergenScreen.java │ ├── datagen │ ├── BaseLootTableProvider.java │ ├── DataGenerators.java │ ├── TutBiomeTags.java │ ├── TutBlockStates.java │ ├── TutBlockTags.java │ ├── TutItemModels.java │ ├── TutItemTags.java │ ├── TutLanguageProvider.java │ ├── TutLootTables.java │ ├── TutRecipes.java │ └── TutStructureSetTags.java │ ├── entities │ ├── AvoidEntityGoalNoCombat.java │ ├── ThiefEntity.java │ ├── ThiefFindChestGoal.java │ ├── ThiefModel.java │ └── ThiefRenderer.java │ ├── manasystem │ ├── ManaConfig.java │ ├── client │ │ ├── ClientManaData.java │ │ ├── KeyBindings.java │ │ ├── KeyInputHandler.java │ │ └── ManaOverlay.java │ ├── data │ │ ├── Mana.java │ │ ├── ManaEvents.java │ │ ├── ManaManager.java │ │ ├── PlayerMana.java │ │ └── PlayerManaProvider.java │ └── network │ │ ├── PacketGatherMana.java │ │ └── PacketSyncManaToClient.java │ ├── setup │ ├── ClientSetup.java │ ├── Config.java │ ├── Messages.java │ ├── ModSetup.java │ └── Registration.java │ ├── varia │ ├── ClientTools.java │ ├── CustomEnergyStorage.java │ └── Tools.java │ └── worldgen │ ├── dimensions │ ├── Dimensions.java │ ├── MysteriousBiomeProvider.java │ └── MysteriousChunkGenerator.java │ ├── ores │ ├── DimensionBiomeFilter.java │ ├── Ores.java │ └── OresConfig.java │ └── structures │ ├── PortalStructure.java │ └── ThiefDenStructure.java └── resources ├── META-INF ├── accesstransformer.cfg └── mods.toml ├── assets └── tutorialv3 │ └── textures │ ├── block │ ├── generator_front.png │ ├── generator_front_powered.png │ ├── generator_front_powered.png.mcmeta │ ├── generator_off.png │ ├── generator_on.png │ ├── generator_side.png │ ├── mysterious_ore_deepslate.png │ ├── mysterious_ore_end.png │ ├── mysterious_ore_nether.png │ ├── mysterious_ore_overworld.png │ ├── portal_side.png │ ├── portal_top.png │ ├── portal_top.png.mcmeta │ ├── powergen_off.png │ ├── powergen_on.png │ ├── powergen_on.png.mcmeta │ └── powergen_window.png │ ├── effect │ └── halo.png │ ├── entity │ └── thief.png │ ├── gui │ └── powergen_gui.png │ └── item │ ├── mysterious_ingot.png │ └── raw_mysterious_chunk.png ├── data └── tutorialv3 │ ├── dimension │ └── mysterious.json │ ├── dimension_type │ └── mysterious.json │ ├── structures │ ├── portal.nbt │ └── thiefden.nbt │ └── worldgen │ ├── configured_structure_feature │ ├── portal.json │ └── thiefden.json │ ├── structure_set │ ├── portal.json │ └── thiefden.json │ └── template_pool │ ├── portal │ └── start_pool.json │ └── thiefden │ └── start_pool.json └── pack.mcmeta /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | 24 | # Files from Forge MDK 25 | forge*changelog.txt 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 McJty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | // These repositories are only for Gradle plugins, put any other repositories in the repository block further below 4 | maven { url = 'https://maven.minecraftforge.net' } 5 | maven { url = 'https://maven.parchmentmc.org' } 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true 10 | classpath 'org.parchmentmc:librarian:1.+' 11 | } 12 | } 13 | apply plugin: 'net.minecraftforge.gradle' 14 | apply plugin: 'org.parchmentmc.librarian.forgegradle' 15 | apply plugin: 'eclipse' 16 | apply plugin: 'maven-publish' 17 | 18 | version = '1.0' 19 | group = 'com.mcjty.tutorialv3' // http://maven.apache.org/guides/mini/guide-naming-conventions.html 20 | archivesBaseName = 'tutorialv3' 21 | 22 | // Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. 23 | java.toolchain.languageVersion = JavaLanguageVersion.of(17) 24 | 25 | println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) 26 | minecraft { 27 | // The mappings can be changed at any time and must be in the following format. 28 | // Channel: Version: 29 | // snapshot YYYYMMDD Snapshot are built nightly. 30 | // stable # Stables are built at the discretion of the MCP team. 31 | // official MCVersion Official field/method names from Mojang mapping files 32 | // 33 | // You must be aware of the Mojang license when using the 'official' mappings. 34 | // See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md 35 | // 36 | // Use non-default mappings at your own risk. They may not always work. 37 | // Simply re-run your setup task after changing the mappings to update your workspace. 38 | mappings channel: 'parchment', version: "1.18.1-2022.03.06-1.18.2" 39 | accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') 40 | 41 | // Default run configurations. 42 | // These can be tweaked, removed, or duplicated as needed. 43 | runs { 44 | client { 45 | workingDirectory project.file('run') 46 | 47 | // Recommended logging data for a userdev environment 48 | // The markers can be added/remove as needed separated by commas. 49 | // "SCAN": For mods scan. 50 | // "REGISTRIES": For firing of registry events. 51 | // "REGISTRYDUMP": For getting the contents of all registries. 52 | property 'forge.logging.markers', 'REGISTRIES' 53 | 54 | // Recommended logging level for the console 55 | // You can set various levels here. 56 | // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels 57 | property 'forge.logging.console.level', 'debug' 58 | 59 | mods { 60 | examplemod { 61 | source sourceSets.main 62 | } 63 | } 64 | } 65 | 66 | server { 67 | workingDirectory project.file('run') 68 | 69 | // Recommended logging data for a userdev environment 70 | // The markers can be added/remove as needed separated by commas. 71 | // "SCAN": For mods scan. 72 | // "REGISTRIES": For firing of registry events. 73 | // "REGISTRYDUMP": For getting the contents of all registries. 74 | property 'forge.logging.markers', 'REGISTRIES' 75 | 76 | // Recommended logging level for the console 77 | // You can set various levels here. 78 | // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels 79 | property 'forge.logging.console.level', 'debug' 80 | 81 | mods { 82 | examplemod { 83 | source sourceSets.main 84 | } 85 | } 86 | } 87 | 88 | data { 89 | workingDirectory project.file('run') 90 | 91 | // Recommended logging data for a userdev environment 92 | // The markers can be added/remove as needed separated by commas. 93 | // "SCAN": For mods scan. 94 | // "REGISTRIES": For firing of registry events. 95 | // "REGISTRYDUMP": For getting the contents of all registries. 96 | property 'forge.logging.markers', 'REGISTRIES' 97 | 98 | // Recommended logging level for the console 99 | // You can set various levels here. 100 | // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels 101 | property 'forge.logging.console.level', 'debug' 102 | 103 | // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. 104 | args '--mod', 'tutorialv3', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') 105 | 106 | mods { 107 | examplemod { 108 | source sourceSets.main 109 | } 110 | } 111 | } 112 | } 113 | } 114 | 115 | // Include resources generated by data generators. 116 | sourceSets.main.resources { srcDir 'src/generated/resources' } 117 | 118 | repositories { 119 | // Put repositories for dependencies here 120 | // ForgeGradle automatically adds the Forge maven and Maven Central for you 121 | 122 | maven { // JEI 123 | url "https://dvs1.progwml6.com/files/maven" 124 | } 125 | maven { // TOP 126 | url "https://cursemaven.com" 127 | } 128 | } 129 | 130 | dependencies { 131 | // Specify the version of Minecraft to use. If this is any group other than 'net.minecraft', it is assumed 132 | // that the dep is a ForgeGradle 'patcher' dependency, and its patches will be applied. 133 | // The userdev artifact is a special name and will get all sorts of transformations applied to it. 134 | minecraft 'net.minecraftforge:forge:1.18.2-40.0.12' 135 | 136 | compileOnly fg.deobf("mezz.jei:jei-${jei_version}:api") 137 | implementation fg.deobf("mezz.jei:jei-${jei_version}") 138 | 139 | implementation fg.deobf("curse.maven:the-one-probe-245211:3550084") 140 | } 141 | 142 | // Example for how to get properties into the manifest for reading at runtime. 143 | jar { 144 | manifest { 145 | attributes([ 146 | "Specification-Title" : "examplemod", 147 | "Specification-Vendor" : "examplemodsareus", 148 | "Specification-Version" : "1", // We are version 1 of ourselves 149 | "Implementation-Title" : project.name, 150 | "Implementation-Version" : project.jar.archiveVersion, 151 | "Implementation-Vendor" : "examplemodsareus", 152 | "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") 153 | ]) 154 | } 155 | } 156 | 157 | // Example configuration to allow publishing using the maven-publish plugin 158 | // This is the preferred method to reobfuscate your jar file 159 | jar.finalizedBy('reobfJar') 160 | // However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing 161 | // publish.dependsOn('reobfJar') 162 | 163 | publishing { 164 | publications { 165 | mavenJava(MavenPublication) { 166 | artifact jar 167 | } 168 | } 169 | repositories { 170 | maven { 171 | url "file://${project.projectDir}/mcmodsrepo" 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | org.gradle.daemon=false 5 | 6 | jei_version=1.18.2:9.5.0.125 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/gradlew -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=down": { 4 | "model": "tutorialv3:block/generator", 5 | "x": 180 6 | }, 7 | "facing=up": { 8 | "model": "tutorialv3:block/generator" 9 | }, 10 | "facing=north": { 11 | "model": "tutorialv3:block/generator", 12 | "x": 90 13 | }, 14 | "facing=south": { 15 | "model": "tutorialv3:block/generator", 16 | "x": 90, 17 | "y": 180 18 | }, 19 | "facing=west": { 20 | "model": "tutorialv3:block/generator", 21 | "x": 90, 22 | "y": 270 23 | }, 24 | "facing=east": { 25 | "model": "tutorialv3:block/generator", 26 | "x": 90, 27 | "y": 90 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/mysterious_ore_deepslate.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "tutorialv3:block/mysterious_ore_deepslate" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/mysterious_ore_end.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "tutorialv3:block/mysterious_ore_end" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/mysterious_ore_nether.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "tutorialv3:block/mysterious_ore_nether" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/mysterious_ore_overworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "tutorialv3:block/mysterious_ore_overworld" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "tutorialv3:block/portal" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/blockstates/powergen.json: -------------------------------------------------------------------------------- 1 | { 2 | "multipart": [ 3 | { 4 | "apply": { 5 | "model": "tutorialv3:block/powergen/main" 6 | } 7 | }, 8 | { 9 | "when": { 10 | "powered": "false" 11 | }, 12 | "apply": { 13 | "model": "tutorialv3:block/powergen/singleoff" 14 | } 15 | }, 16 | { 17 | "when": { 18 | "powered": "false" 19 | }, 20 | "apply": { 21 | "model": "tutorialv3:block/powergen/singleoff", 22 | "x": 180 23 | } 24 | }, 25 | { 26 | "when": { 27 | "powered": "false" 28 | }, 29 | "apply": { 30 | "model": "tutorialv3:block/powergen/singleoff", 31 | "x": 90 32 | } 33 | }, 34 | { 35 | "when": { 36 | "powered": "false" 37 | }, 38 | "apply": { 39 | "model": "tutorialv3:block/powergen/singleoff", 40 | "x": 270 41 | } 42 | }, 43 | { 44 | "when": { 45 | "powered": "false" 46 | }, 47 | "apply": { 48 | "model": "tutorialv3:block/powergen/singleoff", 49 | "x": 90, 50 | "y": 90 51 | } 52 | }, 53 | { 54 | "when": { 55 | "powered": "false" 56 | }, 57 | "apply": { 58 | "model": "tutorialv3:block/powergen/singleoff", 59 | "x": 90, 60 | "y": 270 61 | } 62 | }, 63 | { 64 | "when": { 65 | "powered": "true" 66 | }, 67 | "apply": { 68 | "model": "tutorialv3:block/powergen/singleon" 69 | } 70 | }, 71 | { 72 | "when": { 73 | "powered": "true" 74 | }, 75 | "apply": { 76 | "model": "tutorialv3:block/powergen/singleon", 77 | "x": 180 78 | } 79 | }, 80 | { 81 | "when": { 82 | "powered": "true" 83 | }, 84 | "apply": { 85 | "model": "tutorialv3:block/powergen/singleon", 86 | "x": 90 87 | } 88 | }, 89 | { 90 | "when": { 91 | "powered": "true" 92 | }, 93 | "apply": { 94 | "model": "tutorialv3:block/powergen/singleon", 95 | "x": 270 96 | } 97 | }, 98 | { 99 | "when": { 100 | "powered": "true" 101 | }, 102 | "apply": { 103 | "model": "tutorialv3:block/powergen/singleon", 104 | "x": 90, 105 | "y": 90 106 | } 107 | }, 108 | { 109 | "when": { 110 | "powered": "true" 111 | }, 112 | "apply": { 113 | "model": "tutorialv3:block/powergen/singleon", 114 | "x": 90, 115 | "y": 270 116 | } 117 | } 118 | ] 119 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "block.tutorialv3.generator": "Generator", 3 | "block.tutorialv3.mysterious_ore_deepslate": "Mysterious ore", 4 | "block.tutorialv3.mysterious_ore_end": "Mysterious ore", 5 | "block.tutorialv3.mysterious_ore_nether": "Mysterious ore", 6 | "block.tutorialv3.mysterious_ore_overworld": "Mysterious ore", 7 | "block.tutorialv3.portal": "Mysterious Portal", 8 | "block.tutorialv3.powergen": "Power generator", 9 | "entity.tutorialv3.thief": "Thief", 10 | "item.tutorialv3.mysterious_ingot": "Mysterious Ingot", 11 | "item.tutorialv3.raw_mysterious_chunk": "Mysterious Raw Chunk", 12 | "item.tutorialv3.thief": "Thief Egg", 13 | "itemGroup.tutorialv3": "Tutorial", 14 | "key.categories.tutorial": "Tutorial Keys", 15 | "key.gatherMana": "Gather Mana", 16 | "message.generator": "Generate ores from ingots!", 17 | "message.nomana": "No mana on this location", 18 | "message.powergen": "Power generator generating %s per tick!", 19 | "screen.tutorial.powergen": "Power generator" 20 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube", 3 | "loader": "tutorialv3:generatorloader" 4 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/mysterious_ore_deepslate.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube_all", 3 | "textures": { 4 | "all": "tutorialv3:block/mysterious_ore_deepslate" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/mysterious_ore_end.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube_all", 3 | "textures": { 4 | "all": "tutorialv3:block/mysterious_ore_end" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/mysterious_ore_nether.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube_all", 3 | "textures": { 4 | "all": "tutorialv3:block/mysterious_ore_nether" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/mysterious_ore_overworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube_all", 3 | "textures": { 4 | "all": "tutorialv3:block/mysterious_ore_overworld" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/cube", 3 | "textures": { 4 | "down": "tutorialv3:block/portal_side", 5 | "up": "tutorialv3:block/portal_top", 6 | "north": "tutorialv3:block/portal_side", 7 | "south": "tutorialv3:block/portal_side", 8 | "east": "tutorialv3:block/portal_side", 9 | "west": "tutorialv3:block/portal_side" 10 | } 11 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/powergen/singleoff.json: -------------------------------------------------------------------------------- 1 | { 2 | "textures": { 3 | "single": "tutorialv3:block/powergen_off" 4 | }, 5 | "elements": [ 6 | { 7 | "from": [ 8 | 3, 9 | 3, 10 | 3 11 | ], 12 | "to": [ 13 | 13, 14 | 13, 15 | 13 16 | ], 17 | "faces": { 18 | "down": { 19 | "texture": "#single" 20 | } 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/block/powergen/singleon.json: -------------------------------------------------------------------------------- 1 | { 2 | "textures": { 3 | "single": "tutorialv3:block/powergen_on" 4 | }, 5 | "elements": [ 6 | { 7 | "from": [ 8 | 3, 9 | 3, 10 | 3 11 | ], 12 | "to": [ 13 | 13, 14 | 13, 15 | 13 16 | ], 17 | "faces": { 18 | "down": { 19 | "texture": "#single" 20 | } 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/generator" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/mysterious_ingot.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "tutorialv3:item/mysterious_ingot" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/mysterious_ore_deepslate.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/mysterious_ore_deepslate" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/mysterious_ore_end.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/mysterious_ore_end" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/mysterious_ore_nether.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/mysterious_ore_nether" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/mysterious_ore_overworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/mysterious_ore_overworld" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/portal" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/powergen.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "tutorialv3:block/powergen/main" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/raw_mysterious_chunk.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/generated", 3 | "textures": { 4 | "layer0": "tutorialv3:item/raw_mysterious_chunk" 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/tutorialv3/models/item/thief.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/template_spawn_egg" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/data/forge/tags/blocks/ores.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:mysterious_ore_overworld", 5 | "tutorialv3:mysterious_ore_nether", 6 | "tutorialv3:mysterious_ore_end", 7 | "tutorialv3:mysterious_ore_deepslate" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/forge/tags/items/ingots.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:mysterious_ingot" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/generated/resources/data/forge/tags/items/ores.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:mysterious_ore_overworld", 5 | "tutorialv3:mysterious_ore_nether", 6 | "tutorialv3:mysterious_ore_end", 7 | "tutorialv3:mysterious_ore_deepslate" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/advancements/recipes/tutorialv3/mysterious_ingot1.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:recipes/root", 3 | "rewards": { 4 | "recipes": [ 5 | "minecraft:mysterious_ingot1" 6 | ] 7 | }, 8 | "criteria": { 9 | "has_ore": { 10 | "trigger": "minecraft:inventory_changed", 11 | "conditions": { 12 | "items": [ 13 | { 14 | "tag": "tutorialv3:mysterious_ore" 15 | } 16 | ] 17 | } 18 | }, 19 | "has_the_recipe": { 20 | "trigger": "minecraft:recipe_unlocked", 21 | "conditions": { 22 | "recipe": "minecraft:mysterious_ingot1" 23 | } 24 | } 25 | }, 26 | "requirements": [ 27 | [ 28 | "has_ore", 29 | "has_the_recipe" 30 | ] 31 | ] 32 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/advancements/recipes/tutorialv3/mysterious_ingot2.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:recipes/root", 3 | "rewards": { 4 | "recipes": [ 5 | "minecraft:mysterious_ingot2" 6 | ] 7 | }, 8 | "criteria": { 9 | "has_chunk": { 10 | "trigger": "minecraft:inventory_changed", 11 | "conditions": { 12 | "items": [ 13 | { 14 | "items": [ 15 | "tutorialv3:raw_mysterious_chunk" 16 | ] 17 | } 18 | ] 19 | } 20 | }, 21 | "has_the_recipe": { 22 | "trigger": "minecraft:recipe_unlocked", 23 | "conditions": { 24 | "recipe": "minecraft:mysterious_ingot2" 25 | } 26 | } 27 | }, 28 | "requirements": [ 29 | [ 30 | "has_chunk", 31 | "has_the_recipe" 32 | ] 33 | ] 34 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/recipes/mysterious_ingot1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:smelting", 3 | "ingredient": { 4 | "tag": "tutorialv3:mysterious_ore" 5 | }, 6 | "result": "tutorialv3:mysterious_ingot", 7 | "experience": 1.0, 8 | "cookingtime": 100 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/recipes/mysterious_ingot2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:smelting", 3 | "ingredient": { 4 | "item": "tutorialv3:raw_mysterious_chunk" 5 | }, 6 | "result": "tutorialv3:mysterious_ingot", 7 | "experience": 0.0, 8 | "cookingtime": 100 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:generator", 5 | "tutorialv3:powergen", 6 | "tutorialv3:mysterious_ore_overworld", 7 | "tutorialv3:mysterious_ore_nether", 8 | "tutorialv3:mysterious_ore_end", 9 | "tutorialv3:mysterious_ore_deepslate" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/generated/resources/data/minecraft/tags/blocks/needs_iron_tool.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:generator", 5 | "tutorialv3:powergen", 6 | "tutorialv3:mysterious_ore_overworld", 7 | "tutorialv3:mysterious_ore_nether", 8 | "tutorialv3:mysterious_ore_end", 9 | "tutorialv3:mysterious_ore_deepslate" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/advancements/recipes/tutorialv3/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:recipes/root", 3 | "rewards": { 4 | "recipes": [ 5 | "tutorialv3:generator" 6 | ] 7 | }, 8 | "criteria": { 9 | "mysterious": { 10 | "trigger": "minecraft:inventory_changed", 11 | "conditions": { 12 | "items": [ 13 | { 14 | "items": [ 15 | "tutorialv3:mysterious_ingot" 16 | ] 17 | } 18 | ] 19 | } 20 | }, 21 | "has_the_recipe": { 22 | "trigger": "minecraft:recipe_unlocked", 23 | "conditions": { 24 | "recipe": "tutorialv3:generator" 25 | } 26 | } 27 | }, 28 | "requirements": [ 29 | [ 30 | "mysterious", 31 | "has_the_recipe" 32 | ] 33 | ] 34 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/advancements/recipes/tutorialv3/powergen.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:recipes/root", 3 | "rewards": { 4 | "recipes": [ 5 | "tutorialv3:powergen" 6 | ] 7 | }, 8 | "criteria": { 9 | "mysterious": { 10 | "trigger": "minecraft:inventory_changed", 11 | "conditions": { 12 | "items": [ 13 | { 14 | "items": [ 15 | "tutorialv3:mysterious_ingot" 16 | ] 17 | } 18 | ] 19 | } 20 | }, 21 | "has_the_recipe": { 22 | "trigger": "minecraft:recipe_unlocked", 23 | "conditions": { 24 | "recipe": "tutorialv3:powergen" 25 | } 26 | } 27 | }, 28 | "requirements": [ 29 | [ 30 | "mysterious", 31 | "has_the_recipe" 32 | ] 33 | ] 34 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "generator", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:item", 11 | "functions": [ 12 | { 13 | "function": "minecraft:copy_name", 14 | "source": "block_entity" 15 | }, 16 | { 17 | "function": "minecraft:copy_nbt", 18 | "source": "block_entity", 19 | "ops": [ 20 | { 21 | "source": "Info", 22 | "target": "BlockEntityTag.Info", 23 | "op": "replace" 24 | }, 25 | { 26 | "source": "Inventory", 27 | "target": "BlockEntityTag.Inventory", 28 | "op": "replace" 29 | }, 30 | { 31 | "source": "Energy", 32 | "target": "BlockEntityTag.Energy", 33 | "op": "replace" 34 | } 35 | ] 36 | }, 37 | { 38 | "function": "minecraft:set_contents", 39 | "type": "tutorialv3:generator", 40 | "entries": [ 41 | { 42 | "type": "minecraft:dynamic", 43 | "name": "minecraft:contents" 44 | } 45 | ] 46 | } 47 | ], 48 | "name": "tutorialv3:generator" 49 | } 50 | ] 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/mysterious_ore_deepslate.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "mysterious_ore_deepslate", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:alternatives", 11 | "children": [ 12 | { 13 | "type": "minecraft:item", 14 | "conditions": [ 15 | { 16 | "condition": "minecraft:match_tool", 17 | "predicate": { 18 | "enchantments": [ 19 | { 20 | "enchantment": "minecraft:silk_touch", 21 | "levels": { 22 | "min": 1 23 | } 24 | } 25 | ] 26 | } 27 | } 28 | ], 29 | "name": "tutorialv3:mysterious_ore_deepslate" 30 | }, 31 | { 32 | "type": "minecraft:item", 33 | "functions": [ 34 | { 35 | "function": "minecraft:set_count", 36 | "count": { 37 | "type": "minecraft:uniform", 38 | "min": 1.0, 39 | "max": 3.0 40 | }, 41 | "add": false 42 | }, 43 | { 44 | "function": "minecraft:apply_bonus", 45 | "enchantment": "minecraft:fortune", 46 | "formula": "minecraft:uniform_bonus_count", 47 | "parameters": { 48 | "bonusMultiplier": 1 49 | } 50 | }, 51 | { 52 | "function": "minecraft:explosion_decay" 53 | } 54 | ], 55 | "name": "tutorialv3:raw_mysterious_chunk" 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/mysterious_ore_end.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "mysterious_ore_end", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:alternatives", 11 | "children": [ 12 | { 13 | "type": "minecraft:item", 14 | "conditions": [ 15 | { 16 | "condition": "minecraft:match_tool", 17 | "predicate": { 18 | "enchantments": [ 19 | { 20 | "enchantment": "minecraft:silk_touch", 21 | "levels": { 22 | "min": 1 23 | } 24 | } 25 | ] 26 | } 27 | } 28 | ], 29 | "name": "tutorialv3:mysterious_ore_end" 30 | }, 31 | { 32 | "type": "minecraft:item", 33 | "functions": [ 34 | { 35 | "function": "minecraft:set_count", 36 | "count": { 37 | "type": "minecraft:uniform", 38 | "min": 1.0, 39 | "max": 3.0 40 | }, 41 | "add": false 42 | }, 43 | { 44 | "function": "minecraft:apply_bonus", 45 | "enchantment": "minecraft:fortune", 46 | "formula": "minecraft:uniform_bonus_count", 47 | "parameters": { 48 | "bonusMultiplier": 1 49 | } 50 | }, 51 | { 52 | "function": "minecraft:explosion_decay" 53 | } 54 | ], 55 | "name": "tutorialv3:raw_mysterious_chunk" 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/mysterious_ore_nether.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "mysterious_ore_nether", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:alternatives", 11 | "children": [ 12 | { 13 | "type": "minecraft:item", 14 | "conditions": [ 15 | { 16 | "condition": "minecraft:match_tool", 17 | "predicate": { 18 | "enchantments": [ 19 | { 20 | "enchantment": "minecraft:silk_touch", 21 | "levels": { 22 | "min": 1 23 | } 24 | } 25 | ] 26 | } 27 | } 28 | ], 29 | "name": "tutorialv3:mysterious_ore_nether" 30 | }, 31 | { 32 | "type": "minecraft:item", 33 | "functions": [ 34 | { 35 | "function": "minecraft:set_count", 36 | "count": { 37 | "type": "minecraft:uniform", 38 | "min": 1.0, 39 | "max": 3.0 40 | }, 41 | "add": false 42 | }, 43 | { 44 | "function": "minecraft:apply_bonus", 45 | "enchantment": "minecraft:fortune", 46 | "formula": "minecraft:uniform_bonus_count", 47 | "parameters": { 48 | "bonusMultiplier": 1 49 | } 50 | }, 51 | { 52 | "function": "minecraft:explosion_decay" 53 | } 54 | ], 55 | "name": "tutorialv3:raw_mysterious_chunk" 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/mysterious_ore_overworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "mysterious_ore_overworld", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:alternatives", 11 | "children": [ 12 | { 13 | "type": "minecraft:item", 14 | "conditions": [ 15 | { 16 | "condition": "minecraft:match_tool", 17 | "predicate": { 18 | "enchantments": [ 19 | { 20 | "enchantment": "minecraft:silk_touch", 21 | "levels": { 22 | "min": 1 23 | } 24 | } 25 | ] 26 | } 27 | } 28 | ], 29 | "name": "tutorialv3:mysterious_ore_overworld" 30 | }, 31 | { 32 | "type": "minecraft:item", 33 | "functions": [ 34 | { 35 | "function": "minecraft:set_count", 36 | "count": { 37 | "type": "minecraft:uniform", 38 | "min": 1.0, 39 | "max": 3.0 40 | }, 41 | "add": false 42 | }, 43 | { 44 | "function": "minecraft:apply_bonus", 45 | "enchantment": "minecraft:fortune", 46 | "formula": "minecraft:uniform_bonus_count", 47 | "parameters": { 48 | "bonusMultiplier": 1 49 | } 50 | }, 51 | { 52 | "function": "minecraft:explosion_decay" 53 | } 54 | ], 55 | "name": "tutorialv3:raw_mysterious_chunk" 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/loot_tables/blocks/powergen.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "name": "powergen", 6 | "rolls": 1.0, 7 | "bonus_rolls": 0.0, 8 | "entries": [ 9 | { 10 | "type": "minecraft:item", 11 | "functions": [ 12 | { 13 | "function": "minecraft:copy_name", 14 | "source": "block_entity" 15 | }, 16 | { 17 | "function": "minecraft:copy_nbt", 18 | "source": "block_entity", 19 | "ops": [ 20 | { 21 | "source": "Info", 22 | "target": "BlockEntityTag.Info", 23 | "op": "replace" 24 | }, 25 | { 26 | "source": "Inventory", 27 | "target": "BlockEntityTag.Inventory", 28 | "op": "replace" 29 | }, 30 | { 31 | "source": "Energy", 32 | "target": "BlockEntityTag.Energy", 33 | "op": "replace" 34 | } 35 | ] 36 | }, 37 | { 38 | "function": "minecraft:set_contents", 39 | "type": "tutorialv3:powergen", 40 | "entries": [ 41 | { 42 | "type": "minecraft:dynamic", 43 | "name": "minecraft:contents" 44 | } 45 | ] 46 | } 47 | ], 48 | "name": "tutorialv3:powergen" 49 | } 50 | ] 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/recipes/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "group": "tutorialv3", 4 | "pattern": [ 5 | "mxm", 6 | "x#x", 7 | "#x#" 8 | ], 9 | "key": { 10 | "x": { 11 | "tag": "forge:gems/diamond" 12 | }, 13 | "#": { 14 | "tag": "forge:ingots/iron" 15 | }, 16 | "m": { 17 | "item": "tutorialv3:mysterious_ingot" 18 | } 19 | }, 20 | "result": { 21 | "item": "tutorialv3:generator" 22 | } 23 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/recipes/powergen.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "group": "tutorialv3", 4 | "pattern": [ 5 | "mmm", 6 | "x#x", 7 | "#x#" 8 | ], 9 | "key": { 10 | "x": { 11 | "tag": "forge:dusts/redstone" 12 | }, 13 | "#": { 14 | "tag": "forge:ingots/iron" 15 | }, 16 | "m": { 17 | "item": "tutorialv3:mysterious_ingot" 18 | } 19 | }, 20 | "result": { 21 | "item": "tutorialv3:powergen" 22 | } 23 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/tags/blocks/mysterious_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:mysterious_ore_overworld", 5 | "tutorialv3:mysterious_ore_nether", 6 | "tutorialv3:mysterious_ore_end", 7 | "tutorialv3:mysterious_ore_deepslate" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/tags/items/mysterious_ore.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:mysterious_ore_overworld", 5 | "tutorialv3:mysterious_ore_nether", 6 | "tutorialv3:mysterious_ore_end", 7 | "tutorialv3:mysterious_ore_deepslate" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/tags/worldgen/biome/has_structure/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "minecraft:the_void", 5 | "minecraft:plains", 6 | "minecraft:sunflower_plains", 7 | "minecraft:snowy_plains", 8 | "minecraft:ice_spikes", 9 | "minecraft:desert", 10 | "minecraft:swamp", 11 | "minecraft:forest", 12 | "minecraft:flower_forest", 13 | "minecraft:birch_forest", 14 | "minecraft:dark_forest", 15 | "minecraft:old_growth_birch_forest", 16 | "minecraft:old_growth_pine_taiga", 17 | "minecraft:old_growth_spruce_taiga", 18 | "minecraft:taiga", 19 | "minecraft:snowy_taiga", 20 | "minecraft:savanna", 21 | "minecraft:savanna_plateau", 22 | "minecraft:windswept_hills", 23 | "minecraft:windswept_gravelly_hills", 24 | "minecraft:windswept_forest", 25 | "minecraft:windswept_savanna", 26 | "minecraft:jungle", 27 | "minecraft:sparse_jungle", 28 | "minecraft:bamboo_jungle", 29 | "minecraft:badlands", 30 | "minecraft:eroded_badlands", 31 | "minecraft:wooded_badlands", 32 | "minecraft:meadow", 33 | "minecraft:grove", 34 | "minecraft:snowy_slopes", 35 | "minecraft:frozen_peaks", 36 | "minecraft:jagged_peaks", 37 | "minecraft:stony_peaks", 38 | "minecraft:river", 39 | "minecraft:frozen_river", 40 | "minecraft:beach", 41 | "minecraft:snowy_beach", 42 | "minecraft:stony_shore", 43 | "minecraft:warm_ocean", 44 | "minecraft:lukewarm_ocean", 45 | "minecraft:deep_lukewarm_ocean", 46 | "minecraft:ocean", 47 | "minecraft:deep_ocean", 48 | "minecraft:cold_ocean", 49 | "minecraft:deep_cold_ocean", 50 | "minecraft:frozen_ocean", 51 | "minecraft:deep_frozen_ocean", 52 | "minecraft:mushroom_fields", 53 | "minecraft:dripstone_caves", 54 | "minecraft:lush_caves", 55 | "minecraft:nether_wastes", 56 | "minecraft:warped_forest", 57 | "minecraft:crimson_forest", 58 | "minecraft:soul_sand_valley", 59 | "minecraft:basalt_deltas", 60 | "minecraft:the_end", 61 | "minecraft:end_highlands", 62 | "minecraft:end_midlands", 63 | "minecraft:small_end_islands", 64 | "minecraft:end_barrens" 65 | ] 66 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/tags/worldgen/biome/has_structure/thiefden.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "minecraft:the_void", 5 | "minecraft:plains", 6 | "minecraft:sunflower_plains", 7 | "minecraft:snowy_plains", 8 | "minecraft:ice_spikes", 9 | "minecraft:desert", 10 | "minecraft:swamp", 11 | "minecraft:forest", 12 | "minecraft:flower_forest", 13 | "minecraft:birch_forest", 14 | "minecraft:dark_forest", 15 | "minecraft:old_growth_birch_forest", 16 | "minecraft:old_growth_pine_taiga", 17 | "minecraft:old_growth_spruce_taiga", 18 | "minecraft:taiga", 19 | "minecraft:snowy_taiga", 20 | "minecraft:savanna", 21 | "minecraft:savanna_plateau", 22 | "minecraft:windswept_hills", 23 | "minecraft:windswept_gravelly_hills", 24 | "minecraft:windswept_forest", 25 | "minecraft:windswept_savanna", 26 | "minecraft:jungle", 27 | "minecraft:sparse_jungle", 28 | "minecraft:bamboo_jungle", 29 | "minecraft:badlands", 30 | "minecraft:eroded_badlands", 31 | "minecraft:wooded_badlands", 32 | "minecraft:meadow", 33 | "minecraft:grove", 34 | "minecraft:snowy_slopes", 35 | "minecraft:frozen_peaks", 36 | "minecraft:jagged_peaks", 37 | "minecraft:stony_peaks", 38 | "minecraft:river", 39 | "minecraft:frozen_river", 40 | "minecraft:beach", 41 | "minecraft:snowy_beach", 42 | "minecraft:stony_shore", 43 | "minecraft:warm_ocean", 44 | "minecraft:lukewarm_ocean", 45 | "minecraft:deep_lukewarm_ocean", 46 | "minecraft:ocean", 47 | "minecraft:deep_ocean", 48 | "minecraft:cold_ocean", 49 | "minecraft:deep_cold_ocean", 50 | "minecraft:frozen_ocean", 51 | "minecraft:deep_frozen_ocean", 52 | "minecraft:mushroom_fields", 53 | "minecraft:dripstone_caves", 54 | "minecraft:lush_caves", 55 | "minecraft:nether_wastes", 56 | "minecraft:warped_forest", 57 | "minecraft:crimson_forest", 58 | "minecraft:soul_sand_valley", 59 | "minecraft:basalt_deltas", 60 | "minecraft:the_end", 61 | "minecraft:end_highlands", 62 | "minecraft:end_midlands", 63 | "minecraft:small_end_islands", 64 | "minecraft:end_barrens" 65 | ] 66 | } -------------------------------------------------------------------------------- /src/generated/resources/data/tutorialv3/tags/worldgen/structure_set/mysterious_dimension_structure_set.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "tutorialv3:portal", 5 | "tutorialv3:thiefden" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/TutorialV3.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3; 2 | 3 | import com.example.tutorialv3.setup.Config; 4 | import com.example.tutorialv3.setup.ModSetup; 5 | import com.example.tutorialv3.setup.ClientSetup; 6 | import com.example.tutorialv3.setup.Registration; 7 | import com.example.tutorialv3.worldgen.ores.Ores; 8 | import net.minecraftforge.api.distmarker.Dist; 9 | import net.minecraftforge.eventbus.api.IEventBus; 10 | import net.minecraftforge.fml.DistExecutor; 11 | import net.minecraftforge.fml.common.Mod; 12 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 13 | import org.apache.logging.log4j.LogManager; 14 | import org.apache.logging.log4j.Logger; 15 | 16 | @Mod(TutorialV3.MODID) 17 | public class TutorialV3 { 18 | 19 | public static final Logger LOGGER = LogManager.getLogger(); 20 | public static final String MODID = "tutorialv3"; 21 | 22 | public TutorialV3() { 23 | 24 | // Register the deferred registry 25 | ModSetup.setup(); 26 | Registration.init(); 27 | Config.register(); 28 | 29 | // Register the setup method for modloading 30 | IEventBus modbus = FMLJavaModLoadingContext.get().getModEventBus(); 31 | modbus.addListener(ModSetup::init); 32 | DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> modbus.addListener(ClientSetup::init)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/GeneratorBlock.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import net.minecraft.ChatFormatting; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.network.chat.Component; 7 | import net.minecraft.network.chat.TranslatableComponent; 8 | import net.minecraft.world.InteractionHand; 9 | import net.minecraft.world.InteractionResult; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.minecraft.world.item.BlockItem; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.item.TooltipFlag; 14 | import net.minecraft.world.item.context.BlockPlaceContext; 15 | import net.minecraft.world.level.BlockGetter; 16 | import net.minecraft.world.level.Level; 17 | import net.minecraft.world.level.block.Block; 18 | import net.minecraft.world.level.block.EntityBlock; 19 | import net.minecraft.world.level.block.SoundType; 20 | import net.minecraft.world.level.block.entity.BlockEntity; 21 | import net.minecraft.world.level.block.entity.BlockEntityTicker; 22 | import net.minecraft.world.level.block.entity.BlockEntityType; 23 | import net.minecraft.world.level.block.state.BlockState; 24 | import net.minecraft.world.level.block.state.StateDefinition; 25 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 26 | import net.minecraft.world.level.material.Material; 27 | import net.minecraft.world.phys.BlockHitResult; 28 | import net.minecraft.world.phys.Vec3; 29 | import net.minecraft.world.phys.shapes.CollisionContext; 30 | import net.minecraft.world.phys.shapes.Shapes; 31 | import net.minecraft.world.phys.shapes.VoxelShape; 32 | import org.jetbrains.annotations.Nullable; 33 | 34 | import java.util.List; 35 | 36 | public class GeneratorBlock extends Block implements EntityBlock { 37 | 38 | public static final String MESSAGE_GENERATOR = "message.generator"; 39 | 40 | private static final VoxelShape SHAPE_DOWN = Shapes.box(0, .2, 0, 1, 1, 1); 41 | private static final VoxelShape SHAPE_UP = Shapes.box(0, 0, 0, 1, .8, 1); 42 | private static final VoxelShape SHAPE_NORTH = Shapes.box(0, 0, .2, 1, 1, 1); 43 | private static final VoxelShape SHAPE_SOUTH = Shapes.box(0, 0, 0, 1, 1, .8); 44 | private static final VoxelShape SHAPE_WEST = Shapes.box(.2, 0, 0, 1, 1, 1); 45 | private static final VoxelShape SHAPE_EAST = Shapes.box(0, 0, 0, .8, 1, 1); 46 | 47 | public GeneratorBlock() { 48 | super(Properties.of(Material.METAL) 49 | .sound(SoundType.METAL) 50 | .strength(2.0f) 51 | .noOcclusion() 52 | .requiresCorrectToolForDrops() 53 | ); 54 | } 55 | 56 | @Override 57 | public void appendHoverText(ItemStack stack, @javax.annotation.Nullable BlockGetter reader, List list, TooltipFlag flags) { 58 | list.add(new TranslatableComponent(MESSAGE_GENERATOR).withStyle(ChatFormatting.BLUE)); 59 | } 60 | 61 | @Override 62 | public VoxelShape getShape(BlockState state, BlockGetter getter, BlockPos pos, CollisionContext context) { 63 | return switch (state.getValue(BlockStateProperties.FACING)) { 64 | case DOWN -> SHAPE_DOWN; 65 | case UP -> SHAPE_UP; 66 | case NORTH -> SHAPE_NORTH; 67 | case SOUTH -> SHAPE_SOUTH; 68 | case WEST -> SHAPE_WEST; 69 | case EAST -> SHAPE_EAST; 70 | }; 71 | } 72 | 73 | @Nullable 74 | @Override 75 | public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { 76 | return new GeneratorBE(pos, state); 77 | } 78 | 79 | @Nullable 80 | @Override 81 | public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) { 82 | if (!level.isClientSide()) { 83 | return (lvl, pos, stt, te) -> { 84 | if (te instanceof GeneratorBE generator) generator.tickServer(); 85 | }; 86 | } 87 | return null; 88 | } 89 | 90 | @Override 91 | public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult result) { 92 | if (!level.isClientSide()) { 93 | BlockEntity be = level.getBlockEntity(pos); 94 | if (be instanceof GeneratorBE generator) { 95 | Direction direction = result.getDirection(); 96 | Direction facing = state.getValue(BlockStateProperties.FACING); 97 | // If the face that we hit is the same as the direction that our block is facing we know that we hit the front of the block 98 | if (direction == facing) { 99 | // Subtract the position of our block from the location that we hit to get the relative location in 3D 100 | Vec3 hit = result.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()); 101 | // We want to transform this 3D location to 2D so that we can more easily check which quadrant is hit 102 | double x = getXFromHit(facing, hit); 103 | double y = getYFromHit(facing, hit); 104 | 105 | if (x < .5 && y > .5) { 106 | generator.setCollecting(!generator.isCollecting()); 107 | } else if (x > .5 && y > .5) { 108 | generator.setGenerating(!generator.isGenerating()); 109 | } else if (x > .5 && y < .5) { 110 | ItemStack item = player.getItemInHand(hand); 111 | // If the item that the player is holding is a BlockItem then we get the blockstate from it 112 | // and give that to our block entity 113 | if (item.getItem() instanceof BlockItem blockItem) { 114 | var blockState = blockItem.getBlock().defaultBlockState(); 115 | generator.setGeneratingBlock(blockState); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | return InteractionResult.SUCCESS; 122 | } 123 | 124 | private double getYFromHit(Direction facing, Vec3 hit) { 125 | return switch (facing) { 126 | case UP -> 1 - hit.x; 127 | case DOWN -> 1 - hit.x; 128 | case NORTH -> 1 - hit.x; 129 | case SOUTH -> hit.x; 130 | case WEST -> hit.z; 131 | case EAST -> 1 - hit.z; 132 | }; 133 | } 134 | 135 | private double getXFromHit(Direction facing, Vec3 hit) { 136 | return switch (facing) { 137 | case UP -> hit.z; 138 | case DOWN -> 1 - hit.z; 139 | case NORTH -> hit.y; 140 | case SOUTH -> hit.y; 141 | case WEST -> hit.y; 142 | case EAST -> hit.y; 143 | }; 144 | } 145 | 146 | 147 | @Override 148 | public BlockState getStateForPlacement(BlockPlaceContext context) { 149 | return this.defaultBlockState().setValue(BlockStateProperties.FACING, context.getNearestLookingDirection().getOpposite()); 150 | } 151 | 152 | @Override 153 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 154 | builder.add(BlockStateProperties.FACING); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/GeneratorConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | 5 | public class GeneratorConfig { 6 | 7 | public static ForgeConfigSpec.IntValue COLLECTING_DELAY; 8 | public static ForgeConfigSpec.IntValue INGOTS_PER_ORE; 9 | public static ForgeConfigSpec.IntValue ENERGY_CAPACITY; 10 | public static ForgeConfigSpec.IntValue ENERGY_RECEIVE; 11 | public static ForgeConfigSpec.IntValue ENERGY_GENERATE; 12 | 13 | public static void registerServerConfig(ForgeConfigSpec.Builder SERVER_BUILDER) { 14 | SERVER_BUILDER.comment("Settings for the generator").push("generator"); 15 | 16 | COLLECTING_DELAY = SERVER_BUILDER 17 | .comment("Delay (in ticks) before collecting items") 18 | .defineInRange("collectingDelay", 10, 1, Integer.MAX_VALUE); 19 | INGOTS_PER_ORE = SERVER_BUILDER 20 | .comment("How many ingots you get from one ore") 21 | .defineInRange("ingotsPerOre", 10, 1, Integer.MAX_VALUE); 22 | ENERGY_CAPACITY = SERVER_BUILDER 23 | .comment("How much energy fits into the generator") 24 | .defineInRange("capacity", 100000, 1, Integer.MAX_VALUE); 25 | ENERGY_RECEIVE = SERVER_BUILDER 26 | .comment("How much energy the generator can receive per side") 27 | .defineInRange("receive", 1000, 1, Integer.MAX_VALUE); 28 | ENERGY_GENERATE = SERVER_BUILDER 29 | .comment("How much energy is needed to process one ore block") 30 | .defineInRange("generate", 500, 1, Integer.MAX_VALUE); 31 | 32 | SERVER_BUILDER.pop(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/PortalBlock.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import com.example.tutorialv3.varia.Tools; 4 | import com.example.tutorialv3.worldgen.dimensions.Dimensions; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.resources.ResourceKey; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.server.level.ServerPlayer; 9 | import net.minecraft.world.entity.Entity; 10 | import net.minecraft.world.level.BlockGetter; 11 | import net.minecraft.world.level.Level; 12 | import net.minecraft.world.level.block.Block; 13 | import net.minecraft.world.level.block.SoundType; 14 | import net.minecraft.world.level.block.state.BlockState; 15 | import net.minecraft.world.level.material.Material; 16 | import net.minecraft.world.phys.shapes.CollisionContext; 17 | import net.minecraft.world.phys.shapes.Shapes; 18 | import net.minecraft.world.phys.shapes.VoxelShape; 19 | 20 | public class PortalBlock extends Block { 21 | 22 | // Our block is lower then a normal block. That causes the player to sink in it when he stands on the block 23 | // And that in turn causes our 'entityInside' test to detect the player 24 | private static final VoxelShape SHAPE = Shapes.box(0, 0, 0, 1, .8, 1); 25 | 26 | public PortalBlock() { 27 | super(Properties.of(Material.METAL) 28 | .sound(SoundType.METAL) 29 | .strength(-1.0F, 3600000.0F) 30 | .noDrops()); 31 | } 32 | 33 | @Override 34 | public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { 35 | return SHAPE; 36 | } 37 | 38 | // This works because our block isn't a full block 39 | @Override 40 | public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { 41 | if (entity instanceof ServerPlayer player) { 42 | if (level.dimension().equals(Dimensions.MYSTERIOUS)) { 43 | teleportTo(player, pos.north(), Level.OVERWORLD); 44 | } else { 45 | teleportTo(player, pos.north(), Dimensions.MYSTERIOUS); 46 | } 47 | } 48 | } 49 | 50 | private void teleportTo(ServerPlayer player, BlockPos pos, ResourceKey id) { 51 | ServerLevel world = player.getServer().getLevel(id); 52 | Tools.teleport(player, world, new BlockPos(pos.getX(), pos.getY(), pos.getZ()), true); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/PowergenBE.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import com.example.tutorialv3.setup.Registration; 4 | import com.example.tutorialv3.varia.CustomEnergyStorage; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.Direction; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.world.item.ItemStack; 9 | import net.minecraft.world.item.crafting.RecipeType; 10 | import net.minecraft.world.level.block.Block; 11 | import net.minecraft.world.level.block.entity.BlockEntity; 12 | import net.minecraft.world.level.block.state.BlockState; 13 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 14 | import net.minecraftforge.common.ForgeHooks; 15 | import net.minecraftforge.common.capabilities.Capability; 16 | import net.minecraftforge.common.util.LazyOptional; 17 | import net.minecraftforge.energy.CapabilityEnergy; 18 | import net.minecraftforge.energy.IEnergyStorage; 19 | import net.minecraftforge.items.CapabilityItemHandler; 20 | import net.minecraftforge.items.IItemHandler; 21 | import net.minecraftforge.items.ItemStackHandler; 22 | 23 | import javax.annotation.Nonnull; 24 | import javax.annotation.Nullable; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | public class PowergenBE extends BlockEntity { 28 | 29 | // Never create lazy optionals in getCapability. Always place them as fields in the tile entity: 30 | private final ItemStackHandler itemHandler = createHandler(); 31 | private final LazyOptional handler = LazyOptional.of(() -> itemHandler); 32 | 33 | private final CustomEnergyStorage energyStorage = createEnergy(); 34 | private final LazyOptional energy = LazyOptional.of(() -> energyStorage); 35 | 36 | private int counter; 37 | 38 | public PowergenBE(BlockPos pos, BlockState state) { 39 | super(Registration.POWERGEN_BE.get(), pos, state); 40 | } 41 | 42 | 43 | @Override 44 | public void setRemoved() { 45 | super.setRemoved(); 46 | handler.invalidate(); 47 | energy.invalidate(); 48 | } 49 | 50 | public void tickServer() { 51 | if (counter > 0) { 52 | energyStorage.addEnergy(PowergenConfig.POWERGEN_GENERATE.get()); 53 | counter--; 54 | setChanged(); 55 | } 56 | 57 | if (counter <= 0) { 58 | ItemStack stack = itemHandler.getStackInSlot(0); 59 | int burnTime = ForgeHooks.getBurnTime(stack, RecipeType.SMELTING); 60 | if (burnTime > 0) { 61 | itemHandler.extractItem(0, 1, false); 62 | counter = burnTime; 63 | setChanged(); 64 | } 65 | } 66 | 67 | BlockState blockState = level.getBlockState(worldPosition); 68 | if (blockState.getValue(BlockStateProperties.POWERED) != counter > 0) { 69 | level.setBlock(worldPosition, blockState.setValue(BlockStateProperties.POWERED, counter > 0), 70 | Block.UPDATE_ALL); 71 | } 72 | 73 | sendOutPower(); 74 | } 75 | 76 | private void sendOutPower() { 77 | AtomicInteger capacity = new AtomicInteger(energyStorage.getEnergyStored()); 78 | if (capacity.get() > 0) { 79 | for (Direction direction : Direction.values()) { 80 | BlockEntity be = level.getBlockEntity(worldPosition.relative(direction)); 81 | if (be != null) { 82 | boolean doContinue = be.getCapability(CapabilityEnergy.ENERGY, direction.getOpposite()).map(handler -> { 83 | if (handler.canReceive()) { 84 | int received = handler.receiveEnergy(Math.min(capacity.get(), PowergenConfig.POWERGEN_SEND.get()), false); 85 | capacity.addAndGet(-received); 86 | energyStorage.consumeEnergy(received); 87 | setChanged(); 88 | return capacity.get() > 0; 89 | } else { 90 | return true; 91 | } 92 | } 93 | ).orElse(true); 94 | if (!doContinue) { 95 | return; 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | @Override 103 | public void load(CompoundTag tag) { 104 | if (tag.contains("Inventory")) { 105 | itemHandler.deserializeNBT(tag.getCompound("Inventory")); 106 | } 107 | if (tag.contains("Energy")) { 108 | energyStorage.deserializeNBT(tag.get("Energy")); 109 | } 110 | if (tag.contains("Info")) { 111 | counter = tag.getCompound("Info").getInt("Counter"); 112 | } 113 | super.load(tag); 114 | } 115 | 116 | @Override 117 | public void saveAdditional(CompoundTag tag) { 118 | tag.put("Inventory", itemHandler.serializeNBT()); 119 | tag.put("Energy", energyStorage.serializeNBT()); 120 | 121 | CompoundTag infoTag = new CompoundTag(); 122 | infoTag.putInt("Counter", counter); 123 | tag.put("Info", infoTag); 124 | } 125 | 126 | private ItemStackHandler createHandler() { 127 | return new ItemStackHandler(1) { 128 | 129 | @Override 130 | protected void onContentsChanged(int slot) { 131 | // To make sure the TE persists when the chunk is saved later we need to 132 | // mark it dirty every time the item handler changes 133 | setChanged(); 134 | } 135 | 136 | @Override 137 | public boolean isItemValid(int slot, @Nonnull ItemStack stack) { 138 | return ForgeHooks.getBurnTime(stack, RecipeType.SMELTING) > 0; 139 | } 140 | 141 | @Nonnull 142 | @Override 143 | public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) { 144 | if (ForgeHooks.getBurnTime(stack, RecipeType.SMELTING) <= 0) { 145 | return stack; 146 | } 147 | return super.insertItem(slot, stack, simulate); 148 | } 149 | }; 150 | } 151 | 152 | private CustomEnergyStorage createEnergy() { 153 | return new CustomEnergyStorage(PowergenConfig.POWERGEN_CAPACITY.get(), 0) { 154 | @Override 155 | protected void onEnergyChanged() { 156 | setChanged(); 157 | } 158 | }; 159 | } 160 | 161 | @Nonnull 162 | @Override 163 | public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { 164 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { 165 | return handler.cast(); 166 | } 167 | if (cap == CapabilityEnergy.ENERGY) { 168 | return energy.cast(); 169 | } 170 | return super.getCapability(cap, side); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/PowergenBlock.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import net.minecraft.ChatFormatting; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.network.chat.TranslatableComponent; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraft.world.InteractionHand; 9 | import net.minecraft.world.InteractionResult; 10 | import net.minecraft.world.MenuProvider; 11 | import net.minecraft.world.entity.player.Inventory; 12 | import net.minecraft.world.entity.player.Player; 13 | import net.minecraft.world.inventory.AbstractContainerMenu; 14 | import net.minecraft.world.item.ItemStack; 15 | import net.minecraft.world.item.TooltipFlag; 16 | import net.minecraft.world.item.context.BlockPlaceContext; 17 | import net.minecraft.world.level.BlockGetter; 18 | import net.minecraft.world.level.Level; 19 | import net.minecraft.world.level.block.Block; 20 | import net.minecraft.world.level.block.EntityBlock; 21 | import net.minecraft.world.level.block.SoundType; 22 | import net.minecraft.world.level.block.entity.BlockEntity; 23 | import net.minecraft.world.level.block.entity.BlockEntityTicker; 24 | import net.minecraft.world.level.block.entity.BlockEntityType; 25 | import net.minecraft.world.level.block.state.BlockState; 26 | import net.minecraft.world.level.block.state.StateDefinition; 27 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 28 | import net.minecraft.world.level.material.Material; 29 | import net.minecraft.world.phys.BlockHitResult; 30 | import net.minecraft.world.phys.shapes.Shapes; 31 | import net.minecraft.world.phys.shapes.VoxelShape; 32 | import net.minecraftforge.network.NetworkHooks; 33 | 34 | import javax.annotation.Nullable; 35 | import java.util.List; 36 | 37 | public class PowergenBlock extends Block implements EntityBlock { 38 | 39 | public static final String MESSAGE_POWERGEN = "message.powergen"; 40 | public static final String SCREEN_TUTORIAL_POWERGEN = "screen.tutorial.powergen"; 41 | 42 | private static final VoxelShape RENDER_SHAPE = Shapes.box(0.1, 0.1, 0.1, 0.9, 0.9, 0.9); 43 | 44 | public PowergenBlock() { 45 | super(Properties.of(Material.METAL) 46 | .sound(SoundType.METAL) 47 | .strength(2.0f) 48 | .lightLevel(state -> state.getValue(BlockStateProperties.POWERED) ? 14 : 0) 49 | .requiresCorrectToolForDrops() 50 | ); 51 | } 52 | 53 | @SuppressWarnings("deprecation") 54 | @Override 55 | public VoxelShape getOcclusionShape(BlockState state, BlockGetter reader, BlockPos pos) { 56 | return RENDER_SHAPE; 57 | } 58 | 59 | 60 | @Override 61 | public void appendHoverText(ItemStack stack, @Nullable BlockGetter reader, List list, TooltipFlag flags) { 62 | list.add(new TranslatableComponent(MESSAGE_POWERGEN, Integer.toString(PowergenConfig.POWERGEN_GENERATE.get())) 63 | .withStyle(ChatFormatting.BLUE)); 64 | } 65 | 66 | @Nullable 67 | @Override 68 | public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { 69 | return new PowergenBE(blockPos, blockState); 70 | } 71 | 72 | @Nullable 73 | @Override 74 | public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) { 75 | if (level.isClientSide()) { 76 | return null; 77 | } 78 | return (lvl, pos, blockState, t) -> { 79 | if (t instanceof PowergenBE tile) { 80 | tile.tickServer(); 81 | } 82 | }; 83 | } 84 | 85 | @Override 86 | protected void createBlockStateDefinition(StateDefinition.Builder builder) { 87 | super.createBlockStateDefinition(builder); 88 | builder.add(BlockStateProperties.POWERED); 89 | } 90 | 91 | @Nullable 92 | @Override 93 | public BlockState getStateForPlacement(BlockPlaceContext context) { 94 | return super.getStateForPlacement(context).setValue(BlockStateProperties.POWERED, false); 95 | } 96 | 97 | @SuppressWarnings("deprecation") 98 | @Override 99 | public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult trace) { 100 | if (!level.isClientSide) { 101 | BlockEntity be = level.getBlockEntity(pos); 102 | if (be instanceof PowergenBE) { 103 | MenuProvider containerProvider = new MenuProvider() { 104 | @Override 105 | public Component getDisplayName() { 106 | return new TranslatableComponent(SCREEN_TUTORIAL_POWERGEN); 107 | } 108 | 109 | @Override 110 | public AbstractContainerMenu createMenu(int windowId, Inventory playerInventory, Player playerEntity) { 111 | return new PowergenContainer(windowId, pos, playerInventory, playerEntity); 112 | } 113 | }; 114 | NetworkHooks.openGui((ServerPlayer) player, containerProvider, be.getBlockPos()); 115 | } else { 116 | throw new IllegalStateException("Our named container provider is missing!"); 117 | } 118 | } 119 | return InteractionResult.SUCCESS; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/PowergenConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | 5 | public class PowergenConfig { 6 | 7 | public static ForgeConfigSpec.IntValue POWERGEN_CAPACITY; 8 | public static ForgeConfigSpec.IntValue POWERGEN_GENERATE; 9 | public static ForgeConfigSpec.IntValue POWERGEN_SEND; 10 | 11 | public static ForgeConfigSpec.DoubleValue RENDER_SCALE; 12 | 13 | public static void registerServerConfig(ForgeConfigSpec.Builder SERVER_BUILDER) { 14 | SERVER_BUILDER.comment("Settings for the power generator").push("powergen"); 15 | 16 | POWERGEN_CAPACITY = SERVER_BUILDER 17 | .comment("How much energy fits into the power generator") 18 | .defineInRange("capacity", 50000, 1, Integer.MAX_VALUE); 19 | POWERGEN_GENERATE = SERVER_BUILDER 20 | .comment("How much energy is generated by the power generator") 21 | .defineInRange("generate", 60, 1, Integer.MAX_VALUE); 22 | POWERGEN_SEND = SERVER_BUILDER 23 | .comment("How much energy the power generator will send out to adjacent blocks every tick") 24 | .defineInRange("send", 200, 1, Integer.MAX_VALUE); 25 | 26 | SERVER_BUILDER.pop(); 27 | } 28 | 29 | public static void registerClientConfig(ForgeConfigSpec.Builder CLIENT_BUILDER) { 30 | CLIENT_BUILDER.comment("Client settings for the power generator").push("powergen"); 31 | 32 | RENDER_SCALE = CLIENT_BUILDER 33 | .comment("Scale of the renderer") 34 | .defineInRange("scale", .3, 0.000001, 1000.0); 35 | 36 | CLIENT_BUILDER.pop(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/blocks/PowergenContainer.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.blocks; 2 | 3 | import com.example.tutorialv3.setup.Registration; 4 | import com.example.tutorialv3.varia.CustomEnergyStorage; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.world.entity.player.Inventory; 7 | import net.minecraft.world.entity.player.Player; 8 | import net.minecraft.world.inventory.AbstractContainerMenu; 9 | import net.minecraft.world.inventory.ContainerLevelAccess; 10 | import net.minecraft.world.inventory.DataSlot; 11 | import net.minecraft.world.inventory.Slot; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.item.crafting.RecipeType; 14 | import net.minecraft.world.level.block.entity.BlockEntity; 15 | import net.minecraftforge.common.ForgeHooks; 16 | import net.minecraftforge.energy.CapabilityEnergy; 17 | import net.minecraftforge.energy.IEnergyStorage; 18 | import net.minecraftforge.items.CapabilityItemHandler; 19 | import net.minecraftforge.items.IItemHandler; 20 | import net.minecraftforge.items.SlotItemHandler; 21 | import net.minecraftforge.items.wrapper.InvWrapper; 22 | 23 | public class PowergenContainer extends AbstractContainerMenu { 24 | 25 | private BlockEntity blockEntity; 26 | private Player playerEntity; 27 | private IItemHandler playerInventory; 28 | 29 | public PowergenContainer(int windowId, BlockPos pos, Inventory playerInventory, Player player) { 30 | super(Registration.POWERGEN_CONTAINER.get(), windowId); 31 | blockEntity = player.getCommandSenderWorld().getBlockEntity(pos); 32 | this.playerEntity = player; 33 | this.playerInventory = new InvWrapper(playerInventory); 34 | 35 | if (blockEntity != null) { 36 | blockEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(h -> { 37 | addSlot(new SlotItemHandler(h, 0, 64, 24)); 38 | }); 39 | } 40 | layoutPlayerInventorySlots(10, 70); 41 | trackPower(); 42 | } 43 | 44 | // Setup syncing of power from server to client so that the GUI can show the amount of power in the block 45 | private void trackPower() { 46 | // Unfortunatelly on a dedicated server ints are actually truncated to short so we need 47 | // to split our integer here (split our 32 bit integer into two 16 bit integers) 48 | addDataSlot(new DataSlot() { 49 | @Override 50 | public int get() { 51 | return getEnergy() & 0xffff; 52 | } 53 | 54 | @Override 55 | public void set(int value) { 56 | blockEntity.getCapability(CapabilityEnergy.ENERGY).ifPresent(h -> { 57 | int energyStored = h.getEnergyStored() & 0xffff0000; 58 | ((CustomEnergyStorage)h).setEnergy(energyStored + (value & 0xffff)); 59 | }); 60 | } 61 | }); 62 | addDataSlot(new DataSlot() { 63 | @Override 64 | public int get() { 65 | return (getEnergy() >> 16) & 0xffff; 66 | } 67 | 68 | @Override 69 | public void set(int value) { 70 | blockEntity.getCapability(CapabilityEnergy.ENERGY).ifPresent(h -> { 71 | int energyStored = h.getEnergyStored() & 0x0000ffff; 72 | ((CustomEnergyStorage)h).setEnergy(energyStored | (value << 16)); 73 | }); 74 | } 75 | }); 76 | } 77 | 78 | public int getEnergy() { 79 | return blockEntity.getCapability(CapabilityEnergy.ENERGY).map(IEnergyStorage::getEnergyStored).orElse(0); 80 | } 81 | 82 | @Override 83 | public boolean stillValid(Player playerIn) { 84 | return stillValid(ContainerLevelAccess.create(blockEntity.getLevel(), blockEntity.getBlockPos()), playerEntity, Registration.POWERGEN.get()); 85 | } 86 | 87 | @Override 88 | public ItemStack quickMoveStack(Player playerIn, int index) { 89 | ItemStack itemstack = ItemStack.EMPTY; 90 | Slot slot = this.slots.get(index); 91 | if (slot != null && slot.hasItem()) { 92 | ItemStack stack = slot.getItem(); 93 | itemstack = stack.copy(); 94 | if (index == 0) { 95 | if (!this.moveItemStackTo(stack, 1, 37, true)) { 96 | return ItemStack.EMPTY; 97 | } 98 | slot.onQuickCraft(stack, itemstack); 99 | } else { 100 | if (ForgeHooks.getBurnTime(stack, RecipeType.SMELTING) > 0) { 101 | if (!this.moveItemStackTo(stack, 0, 1, false)) { 102 | return ItemStack.EMPTY; 103 | } 104 | } else if (index < 28) { 105 | if (!this.moveItemStackTo(stack, 28, 37, false)) { 106 | return ItemStack.EMPTY; 107 | } 108 | } else if (index < 37 && !this.moveItemStackTo(stack, 1, 28, false)) { 109 | return ItemStack.EMPTY; 110 | } 111 | } 112 | 113 | if (stack.isEmpty()) { 114 | slot.set(ItemStack.EMPTY); 115 | } else { 116 | slot.setChanged(); 117 | } 118 | 119 | if (stack.getCount() == itemstack.getCount()) { 120 | return ItemStack.EMPTY; 121 | } 122 | 123 | slot.onTake(playerIn, stack); 124 | } 125 | 126 | return itemstack; 127 | } 128 | 129 | 130 | 131 | private int addSlotRange(IItemHandler handler, int index, int x, int y, int amount, int dx) { 132 | for (int i = 0 ; i < amount ; i++) { 133 | addSlot(new SlotItemHandler(handler, index, x, y)); 134 | x += dx; 135 | index++; 136 | } 137 | return index; 138 | } 139 | 140 | private int addSlotBox(IItemHandler handler, int index, int x, int y, int horAmount, int dx, int verAmount, int dy) { 141 | for (int j = 0 ; j < verAmount ; j++) { 142 | index = addSlotRange(handler, index, x, y, horAmount, dx); 143 | y += dy; 144 | } 145 | return index; 146 | } 147 | 148 | private void layoutPlayerInventorySlots(int leftCol, int topRow) { 149 | // Player inventory 150 | addSlotBox(playerInventory, 9, leftCol, topRow, 9, 18, 3, 18); 151 | 152 | // Hotbar 153 | topRow += 58; 154 | addSlotRange(playerInventory, 0, leftCol, topRow, 9, 18); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/client/CustomRenderType.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.client; 2 | 3 | import com.mojang.blaze3d.vertex.DefaultVertexFormat; 4 | import com.mojang.blaze3d.vertex.VertexFormat; 5 | import net.minecraft.client.renderer.RenderType; 6 | 7 | public class CustomRenderType extends RenderType { 8 | 9 | // Dummy 10 | public CustomRenderType(String name, VertexFormat vertexFormat, VertexFormat.Mode mode, int bufferSize, boolean affectsCrumbling, boolean sortOnUpload, Runnable setup, Runnable clear) { 11 | super(name, vertexFormat, mode, bufferSize, affectsCrumbling, sortOnUpload, setup, clear); 12 | } 13 | 14 | private static CompositeState addState(ShaderStateShard shard) { 15 | return CompositeState.builder() 16 | .setLightmapState(LIGHTMAP) 17 | .setShaderState(shard) 18 | .setTextureState(BLOCK_SHEET_MIPPED) 19 | .setTransparencyState(ADDITIVE_TRANSPARENCY) 20 | .setOutputState(TRANSLUCENT_TARGET) 21 | .createCompositeState(true); 22 | } 23 | 24 | public static final RenderType ADD = create("translucent", 25 | DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 26 | 2097152, true, true, 27 | addState(RENDERTYPE_TRANSLUCENT_SHADER)); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/client/GeneratorModelLoader.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.client; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.google.gson.JsonDeserializationContext; 5 | import com.google.gson.JsonObject; 6 | import com.mojang.datafixers.util.Pair; 7 | import net.minecraft.client.renderer.block.model.ItemOverrides; 8 | import net.minecraft.client.renderer.texture.TextureAtlasSprite; 9 | import net.minecraft.client.resources.model.*; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.server.packs.resources.ResourceManager; 12 | import net.minecraftforge.client.ForgeHooksClient; 13 | import net.minecraftforge.client.model.IModelConfiguration; 14 | import net.minecraftforge.client.model.IModelLoader; 15 | import net.minecraftforge.client.model.geometry.IModelGeometry; 16 | 17 | import java.util.Collection; 18 | import java.util.List; 19 | import java.util.Set; 20 | import java.util.function.Function; 21 | 22 | public class GeneratorModelLoader implements IModelLoader { 23 | 24 | public static final ResourceLocation GENERATOR_LOADER = new ResourceLocation(TutorialV3.MODID, "generatorloader"); 25 | 26 | public static final ResourceLocation GENERATOR_FRONT_POWERED = new ResourceLocation(TutorialV3.MODID, "block/generator_front_powered"); 27 | public static final ResourceLocation GENERATOR_FRONT = new ResourceLocation(TutorialV3.MODID, "block/generator_front"); 28 | public static final ResourceLocation GENERATOR_SIDE = new ResourceLocation(TutorialV3.MODID, "block/generator_side"); 29 | public static final ResourceLocation GENERATOR_ON = new ResourceLocation(TutorialV3.MODID, "block/generator_on"); 30 | public static final ResourceLocation GENERATOR_OFF = new ResourceLocation(TutorialV3.MODID, "block/generator_off"); 31 | 32 | public static final Material MATERIAL_FRONT_POWERED = ForgeHooksClient.getBlockMaterial(GENERATOR_FRONT_POWERED); 33 | public static final Material MATERIAL_FRONT = ForgeHooksClient.getBlockMaterial(GENERATOR_FRONT); 34 | public static final Material MATERIAL_SIDE = ForgeHooksClient.getBlockMaterial(GENERATOR_SIDE); 35 | public static final Material MATERIAL_ON = ForgeHooksClient.getBlockMaterial(GENERATOR_ON); 36 | public static final Material MATERIAL_OFF = ForgeHooksClient.getBlockMaterial(GENERATOR_OFF); 37 | 38 | @Override 39 | public void onResourceManagerReload(ResourceManager resourceManager) { 40 | } 41 | 42 | @Override 43 | public GeneratorModelGeometry read(JsonDeserializationContext deserializationContext, JsonObject modelContents) { 44 | return new GeneratorModelGeometry(); 45 | } 46 | 47 | 48 | public static class GeneratorModelGeometry implements IModelGeometry { 49 | 50 | @Override 51 | public BakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function spriteGetter, ModelState modelTransform, ItemOverrides overrides, ResourceLocation modelLocation) { 52 | return new GeneratorBakedModel(modelTransform, spriteGetter, overrides, owner.getCameraTransforms()); 53 | } 54 | 55 | @Override 56 | public Collection getTextures(IModelConfiguration owner, Function modelGetter, Set> missingTextureErrors) { 57 | return List.of(MATERIAL_FRONT, MATERIAL_FRONT_POWERED, MATERIAL_SIDE, MATERIAL_ON, MATERIAL_OFF); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/client/ModelKey.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.client; 2 | 3 | import net.minecraft.client.resources.model.ModelState; 4 | 5 | import javax.annotation.Nullable; 6 | import java.util.Objects; 7 | 8 | /** 9 | * A key used to identify a set of baked quads for the baked model 10 | */ 11 | public record ModelKey(boolean generating, boolean collecting, boolean actuallyGenerating, @Nullable ModelState modelState) { } 12 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/client/PowergenRenderer.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.client; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.blocks.PowergenBE; 5 | import com.example.tutorialv3.blocks.PowergenConfig; 6 | import com.example.tutorialv3.setup.Registration; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import com.mojang.blaze3d.vertex.VertexConsumer; 9 | import com.mojang.math.Matrix4f; 10 | import com.mojang.math.Quaternion; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.renderer.LightTexture; 13 | import net.minecraft.client.renderer.MultiBufferSource; 14 | import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; 15 | import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; 16 | import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; 17 | import net.minecraft.client.renderer.texture.TextureAtlas; 18 | import net.minecraft.client.renderer.texture.TextureAtlasSprite; 19 | import net.minecraft.resources.ResourceLocation; 20 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 21 | 22 | import static java.lang.Boolean.TRUE; 23 | 24 | public class PowergenRenderer implements BlockEntityRenderer { 25 | 26 | public static final ResourceLocation HALO = new ResourceLocation(TutorialV3.MODID, "effect/halo"); 27 | 28 | public PowergenRenderer(BlockEntityRendererProvider.Context context) { 29 | } 30 | 31 | @Override 32 | public void render(PowergenBE powergen, float partialTicks, PoseStack poseStack, MultiBufferSource bufferSource, int combinedLight, int combinedOverlay) { 33 | 34 | Boolean powered = powergen.getBlockState().getValue(BlockStateProperties.POWERED); 35 | if (TRUE != powered) { 36 | return; 37 | } 38 | 39 | int brightness = LightTexture.FULL_BRIGHT; 40 | // To achieve a pulsating effect we use the current time 41 | float s = (System.currentTimeMillis() % 1000) / 1000.0f; 42 | if (s > 0.5f) { 43 | s = 1.0f - s; 44 | } 45 | float scale = 0.1f + s * (float)(double)PowergenConfig.RENDER_SCALE.get(); 46 | 47 | // Get our texture from the atlas 48 | TextureAtlasSprite sprite = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(HALO); 49 | 50 | // Always remember to push the current transformation so that you can restore it later 51 | poseStack.pushPose(); 52 | 53 | // Translate to the middle of the block and 1 unit higher 54 | poseStack.translate(0.5, 1.5, 0.5); 55 | 56 | // Use the orientation of the main camera to make sure the single quad that we are going to render always faces the camera 57 | Quaternion rotation = Minecraft.getInstance().gameRenderer.getMainCamera().rotation(); 58 | poseStack.mulPose(rotation); 59 | 60 | // Actually render the quad in our own custom render type 61 | VertexConsumer buffer = bufferSource.getBuffer(CustomRenderType.ADD); 62 | Matrix4f matrix = poseStack.last().pose(); 63 | // Vertex data has to appear in a specific order: 64 | buffer.vertex(matrix, -scale, -scale, 0.0f).color(1.0f, 1.0f, 1.0f, 0.3f).uv(sprite.getU0(), sprite.getV0()).uv2(brightness).normal(1,0,0).endVertex(); 65 | buffer.vertex(matrix, -scale, scale, 0.0f).color(1.0f, 1.0f, 1.0f, 0.3f).uv(sprite.getU0(), sprite.getV1()).uv2(brightness).normal(1,0,0).endVertex(); 66 | buffer.vertex(matrix, scale, scale, 0.0f).color(1.0f, 1.0f, 1.0f, 0.3f).uv(sprite.getU1(), sprite.getV1()).uv2(brightness).normal(1,0,0).endVertex(); 67 | buffer.vertex(matrix, scale, -scale, 0.0f).color(1.0f, 1.0f, 1.0f, 0.3f).uv(sprite.getU1(), sprite.getV0()).uv2(brightness).normal(1,0,0).endVertex(); 68 | poseStack.popPose(); 69 | } 70 | 71 | public static void register() { 72 | BlockEntityRenderers.register(Registration.POWERGEN_BE.get(), PowergenRenderer::new); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/client/PowergenScreen.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.client; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.blocks.PowergenContainer; 5 | import com.mojang.blaze3d.systems.RenderSystem; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 9 | import net.minecraft.network.chat.Component; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.world.entity.player.Inventory; 12 | 13 | public class PowergenScreen extends AbstractContainerScreen { 14 | 15 | private final ResourceLocation GUI = new ResourceLocation(TutorialV3.MODID, "textures/gui/powergen_gui.png"); 16 | 17 | public PowergenScreen(PowergenContainer container, Inventory inv, Component name) { 18 | super(container, inv, name); 19 | } 20 | 21 | @Override 22 | public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { 23 | this.renderBackground(matrixStack); 24 | super.render(matrixStack, mouseX, mouseY, partialTicks); 25 | this.renderTooltip(matrixStack, mouseX, mouseY); 26 | } 27 | 28 | @Override 29 | protected void renderLabels(PoseStack matrixStack, int mouseX, int mouseY) { 30 | drawString(matrixStack, Minecraft.getInstance().font, "Energy: " + menu.getEnergy(), 10, 10, 0xffffff); 31 | } 32 | 33 | @Override 34 | protected void renderBg(PoseStack matrixStack, float partialTicks, int mouseX, int mouseY) { 35 | RenderSystem.setShaderTexture(0, GUI); 36 | int relX = (this.width - this.imageWidth) / 2; 37 | int relY = (this.height - this.imageHeight) / 2; 38 | this.blit(matrixStack, relX, relY, 0, 0, this.imageWidth, this.imageHeight); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/BaseLootTableProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.minecraft.advancements.critereon.EnchantmentPredicate; 6 | import net.minecraft.advancements.critereon.ItemPredicate; 7 | import net.minecraft.advancements.critereon.MinMaxBounds; 8 | import net.minecraft.data.DataGenerator; 9 | import net.minecraft.data.DataProvider; 10 | import net.minecraft.data.HashCache; 11 | import net.minecraft.data.loot.LootTableProvider; 12 | import net.minecraft.resources.ResourceLocation; 13 | import net.minecraft.world.item.Item; 14 | import net.minecraft.world.item.enchantment.Enchantments; 15 | import net.minecraft.world.level.block.Block; 16 | import net.minecraft.world.level.block.entity.BlockEntityType; 17 | import net.minecraft.world.level.storage.loot.LootPool; 18 | import net.minecraft.world.level.storage.loot.LootTable; 19 | import net.minecraft.world.level.storage.loot.LootTables; 20 | import net.minecraft.world.level.storage.loot.entries.AlternativesEntry; 21 | import net.minecraft.world.level.storage.loot.entries.DynamicLoot; 22 | import net.minecraft.world.level.storage.loot.entries.LootItem; 23 | import net.minecraft.world.level.storage.loot.functions.*; 24 | import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; 25 | import net.minecraft.world.level.storage.loot.predicates.MatchTool; 26 | import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider; 27 | import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; 28 | import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; 29 | import org.apache.logging.log4j.LogManager; 30 | import org.apache.logging.log4j.Logger; 31 | 32 | import java.io.IOException; 33 | import java.nio.file.Path; 34 | import java.util.HashMap; 35 | import java.util.Map; 36 | 37 | public abstract class BaseLootTableProvider extends LootTableProvider { 38 | 39 | private static final Logger LOGGER = LogManager.getLogger(); 40 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); 41 | 42 | protected final Map lootTables = new HashMap<>(); 43 | private final DataGenerator generator; 44 | 45 | public BaseLootTableProvider(DataGenerator dataGeneratorIn) { 46 | super(dataGeneratorIn); 47 | this.generator = dataGeneratorIn; 48 | } 49 | 50 | protected abstract void addTables(); 51 | 52 | protected LootTable.Builder createStandardTable(String name, Block block, BlockEntityType type) { 53 | LootPool.Builder builder = LootPool.lootPool() 54 | .name(name) 55 | .setRolls(ConstantValue.exactly(1)) 56 | .add(LootItem.lootTableItem(block) 57 | .apply(CopyNameFunction.copyName(CopyNameFunction.NameSource.BLOCK_ENTITY)) 58 | .apply(CopyNbtFunction.copyData(ContextNbtProvider.BLOCK_ENTITY) 59 | .copy("Info", "BlockEntityTag.Info", CopyNbtFunction.MergeStrategy.REPLACE) 60 | .copy("Inventory", "BlockEntityTag.Inventory", CopyNbtFunction.MergeStrategy.REPLACE) 61 | .copy("Energy", "BlockEntityTag.Energy", CopyNbtFunction.MergeStrategy.REPLACE)) 62 | .apply(SetContainerContents.setContents(type) 63 | .withEntry(DynamicLoot.dynamicEntry(new ResourceLocation("minecraft", "contents")))) 64 | ); 65 | return LootTable.lootTable().withPool(builder); 66 | } 67 | 68 | protected LootTable.Builder createSimpleTable(String name, Block block) { 69 | LootPool.Builder builder = LootPool.lootPool() 70 | .name(name) 71 | .setRolls(ConstantValue.exactly(1)) 72 | .add(LootItem.lootTableItem(block)); 73 | return LootTable.lootTable().withPool(builder); 74 | } 75 | 76 | protected LootTable.Builder createSilkTouchTable(String name, Block block, Item lootItem, float min, float max) { 77 | LootPool.Builder builder = LootPool.lootPool() 78 | .name(name) 79 | .setRolls(ConstantValue.exactly(1)) 80 | .add(AlternativesEntry.alternatives( 81 | LootItem.lootTableItem(block) 82 | .when(MatchTool.toolMatches(ItemPredicate.Builder.item() 83 | .hasEnchantment(new EnchantmentPredicate(Enchantments.SILK_TOUCH, MinMaxBounds.Ints.atLeast(1))))), 84 | LootItem.lootTableItem(lootItem) 85 | .apply(SetItemCountFunction.setCount(UniformGenerator.between(min, max))) 86 | .apply(ApplyBonusCount.addUniformBonusCount(Enchantments.BLOCK_FORTUNE, 1)) 87 | .apply(ApplyExplosionDecay.explosionDecay()) 88 | ) 89 | ); 90 | return LootTable.lootTable().withPool(builder); 91 | } 92 | 93 | 94 | @Override 95 | public void run(HashCache cache) { 96 | addTables(); 97 | 98 | Map tables = new HashMap<>(); 99 | for (Map.Entry entry : lootTables.entrySet()) { 100 | tables.put(entry.getKey().getLootTable(), entry.getValue().setParamSet(LootContextParamSets.BLOCK).build()); 101 | } 102 | writeTables(cache, tables); 103 | } 104 | 105 | private void writeTables(HashCache cache, Map tables) { 106 | Path outputFolder = this.generator.getOutputFolder(); 107 | tables.forEach((key, lootTable) -> { 108 | Path path = outputFolder.resolve("data/" + key.getNamespace() + "/loot_tables/" + key.getPath() + ".json"); 109 | try { 110 | DataProvider.save(GSON, cache, LootTables.serialize(lootTable), path); 111 | } catch (IOException e) { 112 | LOGGER.error("Couldn't write loot table {}", path, e); 113 | } 114 | }); 115 | } 116 | 117 | @Override 118 | public String getName() { 119 | return "MyTutorial LootTables"; 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/DataGenerators.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import net.minecraft.data.DataGenerator; 5 | import net.minecraftforge.eventbus.api.SubscribeEvent; 6 | import net.minecraftforge.fml.common.Mod; 7 | import net.minecraftforge.forge.event.lifecycle.GatherDataEvent; 8 | 9 | @Mod.EventBusSubscriber(modid = TutorialV3.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) 10 | public class DataGenerators { 11 | 12 | @SubscribeEvent 13 | public static void gatherData(GatherDataEvent event) { 14 | DataGenerator generator = event.getGenerator(); 15 | if (event.includeServer()) { 16 | generator.addProvider(new TutRecipes(generator)); 17 | generator.addProvider(new TutLootTables(generator)); 18 | TutBlockTags blockTags = new TutBlockTags(generator, event.getExistingFileHelper()); 19 | generator.addProvider(blockTags); 20 | generator.addProvider(new TutItemTags(generator, blockTags, event.getExistingFileHelper())); 21 | generator.addProvider(new TutBiomeTags(generator, event.getExistingFileHelper())); 22 | generator.addProvider(new TutStructureSetTags(generator, event.getExistingFileHelper())); 23 | } 24 | if (event.includeClient()) { 25 | generator.addProvider(new TutBlockStates(generator, event.getExistingFileHelper())); 26 | generator.addProvider(new TutItemModels(generator, event.getExistingFileHelper())); 27 | generator.addProvider(new TutLanguageProvider(generator, "en_us")); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutBiomeTags.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.data.BuiltinRegistries; 6 | import net.minecraft.data.DataGenerator; 7 | import net.minecraft.data.tags.TagsProvider; 8 | import net.minecraft.tags.TagManager; 9 | import net.minecraft.world.level.biome.Biome; 10 | import net.minecraftforge.common.data.ExistingFileHelper; 11 | import net.minecraftforge.registries.ForgeRegistries; 12 | 13 | public class TutBiomeTags extends TagsProvider { 14 | 15 | public TutBiomeTags(DataGenerator generator, ExistingFileHelper helper) { 16 | super(generator, BuiltinRegistries.BIOME, TutorialV3.MODID, helper, TagManager.getTagDir(BuiltinRegistries.BIOME.key()).substring(5)); 17 | } 18 | 19 | @Override 20 | protected void addTags() { 21 | ForgeRegistries.BIOMES.getValues().forEach(biome -> { 22 | tag(Registration.HAS_PORTAL).add(biome); 23 | tag(Registration.HAS_THIEFDEN).add(biome); 24 | }); 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return "Tutorial Tags"; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutBlockStates.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.data.DataGenerator; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.world.level.block.Block; 9 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 10 | import net.minecraftforge.client.model.generators.BlockModelBuilder; 11 | import net.minecraftforge.client.model.generators.BlockStateProvider; 12 | import net.minecraftforge.client.model.generators.CustomLoaderBuilder; 13 | import net.minecraftforge.client.model.generators.MultiPartBlockStateBuilder; 14 | import net.minecraftforge.common.data.ExistingFileHelper; 15 | 16 | import static com.example.tutorialv3.client.GeneratorModelLoader.GENERATOR_LOADER; 17 | 18 | public class TutBlockStates extends BlockStateProvider { 19 | 20 | public TutBlockStates(DataGenerator gen, ExistingFileHelper helper) { 21 | super(gen, TutorialV3.MODID, helper); 22 | } 23 | 24 | @Override 25 | protected void registerStatesAndModels() { 26 | registerGenerator(); 27 | registerPowergen(); 28 | registerPortal(); 29 | 30 | simpleBlock(Registration.MYSTERIOUS_ORE_OVERWORLD.get()); 31 | simpleBlock(Registration.MYSTERIOUS_ORE_NETHER.get()); 32 | simpleBlock(Registration.MYSTERIOUS_ORE_END.get()); 33 | simpleBlock(Registration.MYSTERIOUS_ORE_DEEPSLATE.get()); 34 | } 35 | 36 | private void registerPortal() { 37 | Block block = Registration.PORTAL_BLOCK.get(); 38 | ResourceLocation side = modLoc("block/portal_side"); 39 | ResourceLocation top = modLoc("block/portal_top"); 40 | simpleBlock(block, models().cube(block.getRegistryName().getPath(), side, top, side, side, side, side)); 41 | } 42 | 43 | private void registerGenerator() { 44 | // Using CustomLoaderBuilder we can define a json file for our model that will use our baked model 45 | BlockModelBuilder generatorModel = models().getBuilder(Registration.GENERATOR.get().getRegistryName().getPath()) 46 | .parent(models().getExistingFile(mcLoc("cube"))) 47 | .customLoader((blockModelBuilder, helper) -> new CustomLoaderBuilder(GENERATOR_LOADER, blockModelBuilder, helper) { }) 48 | .end(); 49 | directionalBlock(Registration.GENERATOR.get(), generatorModel); 50 | } 51 | 52 | private void registerPowergen() { 53 | BlockModelBuilder frame = models().getBuilder("block/powergen/main"); 54 | frame.parent(models().getExistingFile(mcLoc("cube"))); 55 | 56 | floatingCube(frame, 0f, 0f, 0f, 1f, 16f, 1f); 57 | floatingCube(frame, 15f, 0f, 0f, 16f, 16f, 1f); 58 | floatingCube(frame, 0f, 0f, 15f, 1f, 16f, 16f); 59 | floatingCube(frame, 15f, 0f, 15f, 16f, 16f, 16f); 60 | 61 | floatingCube(frame, 1f, 0f, 0f, 15f, 1f, 1f); 62 | floatingCube(frame, 1f, 15f, 0f, 15f, 16f, 1f); 63 | floatingCube(frame, 1f, 0f, 15f, 15f, 1f, 16f); 64 | floatingCube(frame, 1f, 15f, 15f, 15f, 16f, 16f); 65 | 66 | floatingCube(frame, 0f, 0f, 1f, 1f, 1f, 15f); 67 | floatingCube(frame, 15f, 0f, 1f, 16f, 1f, 15f); 68 | floatingCube(frame, 0f, 15f, 1f, 1f, 16f, 15f); 69 | floatingCube(frame, 15f, 15f, 1f, 16f, 16f, 15f); 70 | 71 | floatingCube(frame, 1f, 1f, 1f, 15f, 15f, 15f); 72 | 73 | frame.texture("window", modLoc("block/powergen_window")); 74 | frame.texture("particle", modLoc("block/powergen_off")); 75 | 76 | createPowergenModel(Registration.POWERGEN.get(), frame); 77 | } 78 | 79 | private void floatingCube(BlockModelBuilder builder, float fx, float fy, float fz, float tx, float ty, float tz) { 80 | builder.element() 81 | .from(fx, fy, fz) 82 | .to(tx, ty, tz) 83 | .allFaces((direction, faceBuilder) -> faceBuilder.texture("#window")) 84 | .end(); 85 | } 86 | 87 | private void createPowergenModel(Block block, BlockModelBuilder frame) { 88 | BlockModelBuilder singleOff = models().getBuilder("block/powergen/singleoff") 89 | .element().from(3, 3, 3).to(13, 13, 13).face(Direction.DOWN).texture("#single").end().end() 90 | .texture("single", modLoc("block/powergen_off")); 91 | BlockModelBuilder singleOn = models().getBuilder("block/powergen/singleon") 92 | .element().from(3, 3, 3).to(13, 13, 13).face(Direction.DOWN).texture("#single").end().end() 93 | .texture("single", modLoc("block/powergen_on")); 94 | 95 | MultiPartBlockStateBuilder bld = getMultipartBuilder(block); 96 | 97 | bld.part().modelFile(frame).addModel(); 98 | 99 | BlockModelBuilder[] models = new BlockModelBuilder[] { singleOff, singleOn }; 100 | for (int i = 0 ; i < 2 ; i++) { 101 | boolean powered = i == 1; 102 | bld.part().modelFile(models[i]).addModel().condition(BlockStateProperties.POWERED, powered); 103 | bld.part().modelFile(models[i]).rotationX(180).addModel().condition(BlockStateProperties.POWERED, powered); 104 | bld.part().modelFile(models[i]).rotationX(90).addModel().condition(BlockStateProperties.POWERED, powered); 105 | bld.part().modelFile(models[i]).rotationX(270).addModel().condition(BlockStateProperties.POWERED, powered); 106 | bld.part().modelFile(models[i]).rotationY(90).rotationX(90).addModel().condition(BlockStateProperties.POWERED, powered); 107 | bld.part().modelFile(models[i]).rotationY(270).rotationX(90).addModel().condition(BlockStateProperties.POWERED, powered); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutBlockTags.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.data.DataGenerator; 6 | import net.minecraft.data.tags.BlockTagsProvider; 7 | import net.minecraft.tags.BlockTags; 8 | import net.minecraftforge.common.Tags; 9 | import net.minecraftforge.common.data.ExistingFileHelper; 10 | 11 | public class TutBlockTags extends BlockTagsProvider { 12 | 13 | public TutBlockTags(DataGenerator generator, ExistingFileHelper helper) { 14 | super(generator, TutorialV3.MODID, helper); 15 | } 16 | 17 | @Override 18 | protected void addTags() { 19 | tag(BlockTags.MINEABLE_WITH_PICKAXE) 20 | .add(Registration.GENERATOR.get()) 21 | .add(Registration.POWERGEN.get()) 22 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD.get()) 23 | .add(Registration.MYSTERIOUS_ORE_NETHER.get()) 24 | .add(Registration.MYSTERIOUS_ORE_END.get()) 25 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE.get()); 26 | tag(BlockTags.NEEDS_IRON_TOOL) 27 | .add(Registration.GENERATOR.get()) 28 | .add(Registration.POWERGEN.get()) 29 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD.get()) 30 | .add(Registration.MYSTERIOUS_ORE_NETHER.get()) 31 | .add(Registration.MYSTERIOUS_ORE_END.get()) 32 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE.get()); 33 | tag(Tags.Blocks.ORES) 34 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD.get()) 35 | .add(Registration.MYSTERIOUS_ORE_NETHER.get()) 36 | .add(Registration.MYSTERIOUS_ORE_END.get()) 37 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE.get()); 38 | 39 | tag(Registration.MYSTERIOUS_ORE) 40 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD.get()) 41 | .add(Registration.MYSTERIOUS_ORE_NETHER.get()) 42 | .add(Registration.MYSTERIOUS_ORE_END.get()) 43 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE.get()); 44 | } 45 | 46 | @Override 47 | public String getName() { 48 | return "Tutorial Tags"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutItemModels.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.data.DataGenerator; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraftforge.client.model.generators.ItemModelProvider; 8 | import net.minecraftforge.common.data.ExistingFileHelper; 9 | 10 | public class TutItemModels extends ItemModelProvider { 11 | 12 | public TutItemModels(DataGenerator generator, ExistingFileHelper existingFileHelper) { 13 | super(generator, TutorialV3.MODID, existingFileHelper); 14 | } 15 | 16 | @Override 17 | protected void registerModels() { 18 | withExistingParent(Registration.MYSTERIOUS_ORE_OVERWORLD_ITEM.get().getRegistryName().getPath(), modLoc("block/mysterious_ore_overworld")); 19 | withExistingParent(Registration.MYSTERIOUS_ORE_NETHER_ITEM.get().getRegistryName().getPath(), modLoc("block/mysterious_ore_nether")); 20 | withExistingParent(Registration.MYSTERIOUS_ORE_END_ITEM.get().getRegistryName().getPath(), modLoc("block/mysterious_ore_end")); 21 | withExistingParent(Registration.MYSTERIOUS_ORE_DEEPSLATE_ITEM.get().getRegistryName().getPath(), modLoc("block/mysterious_ore_deepslate")); 22 | 23 | withExistingParent(Registration.GENERATOR_ITEM.get().getRegistryName().getPath(), modLoc("block/generator")); 24 | withExistingParent(Registration.POWERGEN_ITEM.get().getRegistryName().getPath(), modLoc("block/powergen/main")); 25 | withExistingParent(Registration.PORTAL_ITEM.get().getRegistryName().getPath(), modLoc("block/portal")); 26 | 27 | withExistingParent(Registration.THIEF_EGG.get().getRegistryName().getPath(), mcLoc("item/template_spawn_egg")); 28 | 29 | singleTexture(Registration.RAW_MYSTERIOUS_CHUNK.get().getRegistryName().getPath(), 30 | mcLoc("item/generated"), 31 | "layer0", modLoc("item/raw_mysterious_chunk")); 32 | singleTexture(Registration.MYSTERIOUS_INGOT.get().getRegistryName().getPath(), 33 | mcLoc("item/generated"), 34 | "layer0", modLoc("item/mysterious_ingot")); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutItemTags.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.data.DataGenerator; 6 | import net.minecraft.data.tags.BlockTagsProvider; 7 | import net.minecraft.data.tags.ItemTagsProvider; 8 | import net.minecraftforge.common.Tags; 9 | import net.minecraftforge.common.data.ExistingFileHelper; 10 | 11 | public class TutItemTags extends ItemTagsProvider { 12 | 13 | public TutItemTags(DataGenerator generator, BlockTagsProvider blockTags, ExistingFileHelper helper) { 14 | super(generator, blockTags, TutorialV3.MODID, helper); 15 | } 16 | 17 | @Override 18 | protected void addTags() { 19 | tag(Tags.Items.ORES) 20 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD_ITEM.get()) 21 | .add(Registration.MYSTERIOUS_ORE_NETHER_ITEM.get()) 22 | .add(Registration.MYSTERIOUS_ORE_END_ITEM.get()) 23 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE_ITEM.get()); 24 | tag(Tags.Items.INGOTS) 25 | .add(Registration.MYSTERIOUS_INGOT.get()); 26 | tag(Registration.MYSTERIOUS_ORE_ITEM) 27 | .add(Registration.MYSTERIOUS_ORE_OVERWORLD_ITEM.get()) 28 | .add(Registration.MYSTERIOUS_ORE_NETHER_ITEM.get()) 29 | .add(Registration.MYSTERIOUS_ORE_END_ITEM.get()) 30 | .add(Registration.MYSTERIOUS_ORE_DEEPSLATE_ITEM.get()); 31 | } 32 | 33 | @Override 34 | public String getName() { 35 | return "Tutorial Tags"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutLanguageProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.manasystem.client.KeyBindings; 5 | import com.example.tutorialv3.manasystem.network.PacketGatherMana; 6 | import com.example.tutorialv3.setup.Registration; 7 | import net.minecraft.data.DataGenerator; 8 | import net.minecraftforge.common.data.LanguageProvider; 9 | 10 | import static com.example.tutorialv3.blocks.GeneratorBlock.MESSAGE_GENERATOR; 11 | import static com.example.tutorialv3.blocks.PowergenBlock.MESSAGE_POWERGEN; 12 | import static com.example.tutorialv3.blocks.PowergenBlock.SCREEN_TUTORIAL_POWERGEN; 13 | import static com.example.tutorialv3.setup.ModSetup.TAB_NAME; 14 | 15 | public class TutLanguageProvider extends LanguageProvider { 16 | 17 | public TutLanguageProvider(DataGenerator gen, String locale) { 18 | super(gen, TutorialV3.MODID, locale); 19 | } 20 | 21 | @Override 22 | protected void addTranslations() { 23 | add("itemGroup." + TAB_NAME, "Tutorial"); 24 | add(MESSAGE_POWERGEN, "Power generator generating %s per tick!"); 25 | add(MESSAGE_GENERATOR, "Generate ores from ingots!"); 26 | add(SCREEN_TUTORIAL_POWERGEN, "Power generator"); 27 | 28 | add(Registration.GENERATOR.get(), "Generator"); 29 | add(Registration.POWERGEN.get(), "Power generator"); 30 | add(Registration.PORTAL_BLOCK.get(), "Mysterious Portal"); 31 | 32 | add(Registration.MYSTERIOUS_ORE_OVERWORLD.get(), "Mysterious ore"); 33 | add(Registration.MYSTERIOUS_ORE_NETHER.get(), "Mysterious ore"); 34 | add(Registration.MYSTERIOUS_ORE_END.get(), "Mysterious ore"); 35 | add(Registration.MYSTERIOUS_ORE_DEEPSLATE.get(), "Mysterious ore"); 36 | 37 | add(Registration.RAW_MYSTERIOUS_CHUNK.get(), "Mysterious Raw Chunk"); 38 | add(Registration.MYSTERIOUS_INGOT.get(), "Mysterious Ingot"); 39 | add(Registration.THIEF_EGG.get(), "Thief Egg"); 40 | 41 | add(Registration.THIEF.get(), "Thief"); 42 | 43 | add(KeyBindings.KEY_CATEGORIES_TUTORIAL, "Tutorial Keys"); 44 | add(KeyBindings.KEY_GATHER_MANA, "Gather Mana"); 45 | add(PacketGatherMana.MESSAGE_NO_MANA, "No mana on this location"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutLootTables.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.setup.Registration; 4 | import net.minecraft.data.DataGenerator; 5 | 6 | public class TutLootTables extends BaseLootTableProvider { 7 | 8 | public TutLootTables(DataGenerator dataGeneratorIn) { 9 | super(dataGeneratorIn); 10 | } 11 | 12 | @Override 13 | protected void addTables() { 14 | lootTables.put(Registration.GENERATOR.get(), createStandardTable("generator", Registration.GENERATOR.get(), Registration.GENERATOR_BE.get())); 15 | lootTables.put(Registration.POWERGEN.get(), createStandardTable("powergen", Registration.POWERGEN.get(), Registration.POWERGEN_BE.get())); 16 | lootTables.put(Registration.MYSTERIOUS_ORE_OVERWORLD.get(), createSilkTouchTable("mysterious_ore_overworld", Registration.MYSTERIOUS_ORE_OVERWORLD.get(), Registration.RAW_MYSTERIOUS_CHUNK.get(), 1, 3)); 17 | lootTables.put(Registration.MYSTERIOUS_ORE_NETHER.get(), createSilkTouchTable("mysterious_ore_nether", Registration.MYSTERIOUS_ORE_NETHER.get(), Registration.RAW_MYSTERIOUS_CHUNK.get(), 1, 3)); 18 | lootTables.put(Registration.MYSTERIOUS_ORE_END.get(), createSilkTouchTable("mysterious_ore_end", Registration.MYSTERIOUS_ORE_END.get(), Registration.RAW_MYSTERIOUS_CHUNK.get(), 1, 3)); 19 | lootTables.put(Registration.MYSTERIOUS_ORE_DEEPSLATE.get(), createSilkTouchTable("mysterious_ore_deepslate", Registration.MYSTERIOUS_ORE_DEEPSLATE.get(), Registration.RAW_MYSTERIOUS_CHUNK.get(), 1, 3)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutRecipes.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.setup.Registration; 4 | import net.minecraft.advancements.critereon.InventoryChangeTrigger; 5 | import net.minecraft.advancements.critereon.ItemPredicate; 6 | import net.minecraft.data.DataGenerator; 7 | import net.minecraft.data.recipes.FinishedRecipe; 8 | import net.minecraft.data.recipes.RecipeProvider; 9 | import net.minecraft.data.recipes.ShapedRecipeBuilder; 10 | import net.minecraft.data.recipes.SimpleCookingRecipeBuilder; 11 | import net.minecraft.world.item.crafting.Ingredient; 12 | import net.minecraftforge.common.Tags; 13 | 14 | import java.util.function.Consumer; 15 | 16 | public class TutRecipes extends RecipeProvider { 17 | 18 | public TutRecipes(DataGenerator generatorIn) { 19 | super(generatorIn); 20 | } 21 | 22 | @Override 23 | protected void buildCraftingRecipes(Consumer consumer) { 24 | 25 | ShapedRecipeBuilder.shaped(Registration.GENERATOR.get()) 26 | .pattern("mxm") 27 | .pattern("x#x") 28 | .pattern("#x#") 29 | .define('x', Tags.Items.GEMS_DIAMOND) 30 | .define('#', Tags.Items.INGOTS_IRON) 31 | .define('m', Registration.MYSTERIOUS_INGOT.get()) 32 | .group("tutorialv3") 33 | .unlockedBy("mysterious", InventoryChangeTrigger.TriggerInstance.hasItems(Registration.MYSTERIOUS_INGOT.get())) 34 | .save(consumer); 35 | ShapedRecipeBuilder.shaped(Registration.POWERGEN.get()) 36 | .pattern("mmm") 37 | .pattern("x#x") 38 | .pattern("#x#") 39 | .define('x', Tags.Items.DUSTS_REDSTONE) 40 | .define('#', Tags.Items.INGOTS_IRON) 41 | .define('m', Registration.MYSTERIOUS_INGOT.get()) 42 | .group("tutorialv3") 43 | .unlockedBy("mysterious", InventoryChangeTrigger.TriggerInstance.hasItems(Registration.MYSTERIOUS_INGOT.get())) 44 | .save(consumer); 45 | 46 | SimpleCookingRecipeBuilder.smelting(Ingredient.of(Registration.MYSTERIOUS_ORE_ITEM), 47 | Registration.MYSTERIOUS_INGOT.get(), 1.0f, 100) 48 | .unlockedBy("has_ore", inventoryTrigger(ItemPredicate.Builder.item().of(Registration.MYSTERIOUS_ORE_ITEM).build())) 49 | .save(consumer, "mysterious_ingot1"); 50 | 51 | SimpleCookingRecipeBuilder.smelting(Ingredient.of(Registration.RAW_MYSTERIOUS_CHUNK.get()), 52 | Registration.MYSTERIOUS_INGOT.get(), 0.0f, 100) 53 | .unlockedBy("has_chunk", has(Registration.RAW_MYSTERIOUS_CHUNK.get())) 54 | .save(consumer, "mysterious_ingot2"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/datagen/TutStructureSetTags.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.datagen; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.setup.Registration; 5 | import net.minecraft.data.BuiltinRegistries; 6 | import net.minecraft.data.DataGenerator; 7 | import net.minecraft.data.tags.TagsProvider; 8 | import net.minecraft.resources.ResourceKey; 9 | import net.minecraft.resources.ResourceLocation; 10 | import net.minecraft.tags.TagManager; 11 | import net.minecraft.world.level.levelgen.structure.StructureSet; 12 | import net.minecraftforge.common.data.ExistingFileHelper; 13 | 14 | public class TutStructureSetTags extends TagsProvider { 15 | 16 | public TutStructureSetTags(DataGenerator generator, ExistingFileHelper helper) { 17 | super(generator, BuiltinRegistries.STRUCTURE_SETS, TutorialV3.MODID, helper, TagManager.getTagDir(BuiltinRegistries.STRUCTURE_SETS.key()).substring(5)); 18 | } 19 | 20 | @Override 21 | protected void addTags() { 22 | tag(Registration.MYSTERIOUS_DIMENSION_STRUCTURE_SET) 23 | .add(ResourceKey.create(BuiltinRegistries.STRUCTURE_SETS.key(), new ResourceLocation(TutorialV3.MODID, "portal"))) 24 | .add(ResourceKey.create(BuiltinRegistries.STRUCTURE_SETS.key(), new ResourceLocation(TutorialV3.MODID, "thiefden"))) 25 | ; 26 | } 27 | 28 | @Override 29 | public String getName() { 30 | return "Tutorial Tags"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/entities/AvoidEntityGoalNoCombat.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.entities; 2 | 3 | import net.minecraft.world.entity.EntitySelector; 4 | import net.minecraft.world.entity.LivingEntity; 5 | import net.minecraft.world.entity.PathfinderMob; 6 | import net.minecraft.world.entity.ai.goal.Goal; 7 | import net.minecraft.world.entity.ai.navigation.PathNavigation; 8 | import net.minecraft.world.entity.ai.targeting.TargetingConditions; 9 | import net.minecraft.world.entity.ai.util.DefaultRandomPos; 10 | import net.minecraft.world.level.pathfinder.Path; 11 | import net.minecraft.world.phys.Vec3; 12 | 13 | import javax.annotation.Nullable; 14 | import java.util.EnumSet; 15 | import java.util.List; 16 | 17 | public class AvoidEntityGoalNoCombat extends Goal { 18 | protected final PathfinderMob mob; 19 | private final double walkSpeedModifier; 20 | private final double sprintSpeedModifier; 21 | @Nullable 22 | protected T toAvoid; 23 | protected final float maxDist; 24 | @Nullable 25 | protected Path path; 26 | protected final PathNavigation pathNav; 27 | /** Class of entity this behavior seeks to avoid */ 28 | protected final Class avoidClass; 29 | private final TargetingConditions avoidEntityTargeting; 30 | 31 | /** 32 | * Goal that helps mobs avoid mobs of a specific class 33 | */ 34 | public AvoidEntityGoalNoCombat(PathfinderMob pMob, Class entityClassToAvoid, float maxDistance, double walkSpeedModifier, double sprintSpeedModifier) { 35 | this.mob = pMob; 36 | this.avoidClass = entityClassToAvoid; 37 | this.maxDist = maxDistance; 38 | this.walkSpeedModifier = walkSpeedModifier; 39 | this.sprintSpeedModifier = sprintSpeedModifier; 40 | this.pathNav = pMob.getNavigation(); 41 | this.setFlags(EnumSet.of(Flag.MOVE)); 42 | this.avoidEntityTargeting = TargetingConditions.forNonCombat().range(maxDistance).selector(EntitySelector.NO_CREATIVE_OR_SPECTATOR::test); 43 | } 44 | 45 | /** 46 | * Returns whether execution should begin. You can also read and cache any state necessary for execution in this 47 | * method as well. 48 | */ 49 | public boolean canUse() { 50 | List entitiesOfClass = this.mob.level.getEntitiesOfClass(this.avoidClass, this.mob.getBoundingBox().inflate(this.maxDist, 3.0D, this.maxDist), (ent) -> true); 51 | this.toAvoid = this.mob.level.getNearestEntity(entitiesOfClass, this.avoidEntityTargeting, this.mob, this.mob.getX(), this.mob.getY(), this.mob.getZ()); 52 | if (this.toAvoid == null) { 53 | return false; 54 | } else { 55 | Vec3 vec3 = DefaultRandomPos.getPosAway(this.mob, 16, 7, this.toAvoid.position()); 56 | if (vec3 == null) { 57 | return false; 58 | } else if (this.toAvoid.distanceToSqr(vec3.x, vec3.y, vec3.z) < this.toAvoid.distanceToSqr(this.mob)) { 59 | return false; 60 | } else { 61 | this.path = this.pathNav.createPath(vec3.x, vec3.y, vec3.z, 0); 62 | return this.path != null; 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Returns whether an in-progress EntityAIBase should continue executing 69 | */ 70 | public boolean canContinueToUse() { 71 | return !this.pathNav.isDone(); 72 | } 73 | 74 | /** 75 | * Execute a one shot task or start executing a continuous task 76 | */ 77 | public void start() { 78 | this.pathNav.moveTo(this.path, this.walkSpeedModifier); 79 | } 80 | 81 | /** 82 | * Reset the task's internal state. Called when this task is interrupted by another one 83 | */ 84 | public void stop() { 85 | this.toAvoid = null; 86 | } 87 | 88 | /** 89 | * Keep ticking a continuous task that has already been started 90 | */ 91 | public void tick() { 92 | if (this.mob.distanceToSqr(this.toAvoid) < 49.0D) { 93 | this.mob.getNavigation().setSpeedModifier(this.sprintSpeedModifier); 94 | } else { 95 | this.mob.getNavigation().setSpeedModifier(this.walkSpeedModifier); 96 | } 97 | 98 | } 99 | } -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/entities/ThiefEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.entities; 2 | 3 | import net.minecraft.nbt.CompoundTag; 4 | import net.minecraft.server.level.ServerLevel; 5 | import net.minecraft.world.entity.AgeableMob; 6 | import net.minecraft.world.entity.EntityType; 7 | import net.minecraft.world.entity.LivingEntity; 8 | import net.minecraft.world.entity.ai.attributes.AttributeSupplier; 9 | import net.minecraft.world.entity.ai.attributes.Attributes; 10 | import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; 11 | import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; 12 | import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; 13 | import net.minecraft.world.entity.animal.Animal; 14 | import net.minecraft.world.entity.player.Player; 15 | import net.minecraft.world.level.Level; 16 | 17 | import javax.annotation.Nullable; 18 | 19 | public class ThiefEntity extends Animal { 20 | 21 | private boolean stealing = false; 22 | 23 | public ThiefEntity(EntityType type, Level worldIn) { 24 | super(type, worldIn); 25 | } 26 | 27 | @Override 28 | protected void registerGoals() { 29 | this.goalSelector.addGoal(0, new AvoidEntityGoalNoCombat<>(this, Player.class, 6.0F, 1.2, 1.2)); 30 | this.goalSelector.addGoal(1, new ThiefFindChestGoal(this, 1.3)); 31 | this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8)); 32 | this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F)); 33 | this.goalSelector.addGoal(6, new RandomLookAroundGoal(this)); 34 | } 35 | 36 | @Nullable 37 | @Override 38 | public AgeableMob getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) { 39 | return null; 40 | } 41 | 42 | @Override 43 | public void load(CompoundTag tag) { 44 | super.load(tag); 45 | stealing = tag.getBoolean("Stealing"); 46 | } 47 | 48 | @Override 49 | public boolean save(CompoundTag tag) { 50 | tag.putBoolean("Stealing", stealing); 51 | return super.save(tag); 52 | } 53 | 54 | public boolean isStealing() { 55 | return stealing; 56 | } 57 | 58 | public void setStealing(boolean stealing) { 59 | this.stealing = stealing; 60 | } 61 | 62 | // @Override 63 | // public Packet getAddEntityPacket() { 64 | // return NetworkHooks.getEntitySpawningPacket(this); 65 | // } 66 | 67 | public static AttributeSupplier.Builder prepareAttributes() { 68 | return LivingEntity.createLivingAttributes() 69 | .add(Attributes.ATTACK_DAMAGE, 3.0) 70 | .add(Attributes.MAX_HEALTH, 20.0) 71 | .add(Attributes.FOLLOW_RANGE, 40.0) 72 | .add(Attributes.MOVEMENT_SPEED, 0.3); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/entities/ThiefFindChestGoal.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.entities; 2 | 3 | import com.example.tutorialv3.varia.Tools; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.core.Direction; 6 | import net.minecraft.world.entity.ai.goal.MoveToBlockGoal; 7 | import net.minecraft.world.item.ItemStack; 8 | import net.minecraft.world.level.LevelReader; 9 | import net.minecraft.world.level.block.Blocks; 10 | import net.minecraft.world.level.block.entity.BlockEntity; 11 | import net.minecraft.world.level.block.entity.ChestBlockEntity; 12 | import net.minecraft.world.level.block.state.BlockState; 13 | import net.minecraftforge.items.CapabilityItemHandler; 14 | 15 | import java.util.Random; 16 | 17 | public class ThiefFindChestGoal extends MoveToBlockGoal { 18 | 19 | private final ThiefEntity thief; 20 | private final Random random = new Random(); 21 | 22 | private int stealingCounter = 20; 23 | 24 | public ThiefFindChestGoal(ThiefEntity mob, double pSpeedModifier) { 25 | super(mob, pSpeedModifier, 16); 26 | this.thief = mob; 27 | } 28 | 29 | /** 30 | * Reset the task's internal state. Called when this task is interrupted by another one 31 | */ 32 | public void stop() { 33 | super.stop(); 34 | thief.setStealing(false); 35 | BlockEntity be = mob.level.getBlockEntity(blockPos); 36 | if (be instanceof ChestBlockEntity) { 37 | mob.level.blockEvent(blockPos, be.getBlockState().getBlock(), 1, 0); 38 | } 39 | } 40 | 41 | public void tick() { 42 | super.tick(); 43 | if (isReachedTarget()) { 44 | BlockEntity be = mob.level.getBlockEntity(blockPos); 45 | if (be instanceof ChestBlockEntity chest) { 46 | if (thief.isStealing()) { 47 | stealingCounter--; 48 | if (stealingCounter <= 0) { 49 | stealingCounter = 20; 50 | ItemStack stack = extractRandomItem(chest); 51 | if (!stack.isEmpty()) { 52 | Tools.spawnInWorld(mob.level, blockPos.above(), stack); 53 | } 54 | } 55 | } else { 56 | mob.level.blockEvent(blockPos, be.getBlockState().getBlock(), 1, 1); 57 | stealingCounter = 20; 58 | thief.setStealing(true); 59 | } 60 | } 61 | } 62 | } 63 | 64 | private ItemStack extractRandomItem(BlockEntity e) { 65 | return e.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.UP).map(handler -> { 66 | for (int i = 0 ; i < handler.getSlots() ; i++) { 67 | ItemStack stack = handler.getStackInSlot(i); 68 | if (!stack.isEmpty()) { 69 | if (random.nextFloat() < .3f) { 70 | return handler.extractItem(i, 1, false); 71 | } 72 | } 73 | } 74 | return ItemStack.EMPTY; 75 | }).orElse(ItemStack.EMPTY); 76 | } 77 | 78 | /** 79 | * Return true to set given position as destination 80 | */ 81 | protected boolean isValidTarget(LevelReader pLevel, BlockPos pPos) { 82 | if (!pLevel.isEmptyBlock(pPos.above())) { 83 | return false; 84 | } else { 85 | BlockState blockstate = pLevel.getBlockState(pPos); 86 | return blockstate.is(Blocks.CHEST); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/entities/ThiefModel.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.entities; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import net.minecraft.client.model.HumanoidModel; 5 | import net.minecraft.client.model.geom.ModelLayerLocation; 6 | import net.minecraft.client.model.geom.ModelPart; 7 | import net.minecraft.client.model.geom.builders.CubeDeformation; 8 | import net.minecraft.client.model.geom.builders.LayerDefinition; 9 | import net.minecraft.client.model.geom.builders.MeshDefinition; 10 | import net.minecraft.resources.ResourceLocation; 11 | 12 | public class ThiefModel extends HumanoidModel { 13 | 14 | public static final String BODY = "body"; 15 | 16 | public static ModelLayerLocation THIEF_LAYER = new ModelLayerLocation(new ResourceLocation(TutorialV3.MODID, "thief"), BODY); 17 | 18 | public static LayerDefinition createBodyLayer() { 19 | MeshDefinition meshdefinition = createMesh(CubeDeformation.NONE, 0.6f); 20 | return LayerDefinition.create(meshdefinition, 64, 32); 21 | } 22 | 23 | public ThiefModel(ModelPart part) { 24 | super(part); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/entities/ThiefRenderer.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.entities; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 5 | import net.minecraft.client.renderer.entity.HumanoidMobRenderer; 6 | import net.minecraft.resources.ResourceLocation; 7 | 8 | import javax.annotation.Nonnull; 9 | 10 | public class ThiefRenderer extends HumanoidMobRenderer { 11 | 12 | private static final ResourceLocation TEXTURE = new ResourceLocation(TutorialV3.MODID, "textures/entity/thief.png"); 13 | 14 | public ThiefRenderer(EntityRendererProvider.Context context) { 15 | super(context, new ThiefModel(context.bakeLayer(ThiefModel.THIEF_LAYER)), 1f); 16 | } 17 | 18 | @Nonnull 19 | @Override 20 | public ResourceLocation getTextureLocation(ThiefEntity entity) { 21 | return TEXTURE; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/ManaConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | 5 | public class ManaConfig { 6 | 7 | public static ForgeConfigSpec.IntValue CHUNK_MIN_MANA; 8 | public static ForgeConfigSpec.IntValue CHUNK_MAX_MANA; 9 | 10 | public static ForgeConfigSpec.IntValue MANA_HUD_X; 11 | public static ForgeConfigSpec.IntValue MANA_HUD_Y; 12 | public static ForgeConfigSpec.IntValue MANA_HUD_COLOR; 13 | 14 | public static void registerServerConfig(ForgeConfigSpec.Builder SERVER_BUILDER) { 15 | SERVER_BUILDER.comment("Settings for the mana system").push("mana"); 16 | 17 | CHUNK_MIN_MANA = SERVER_BUILDER 18 | .comment("Minumum amount of mana in a chunk") 19 | .defineInRange("minMana", 10, 0, Integer.MAX_VALUE); 20 | CHUNK_MAX_MANA = SERVER_BUILDER 21 | .comment("Maximum amount of mana in a chunk (relative to minMana)") 22 | .defineInRange("maxMana", 100, 1, Integer.MAX_VALUE); 23 | 24 | SERVER_BUILDER.pop(); 25 | } 26 | 27 | public static void registerClientConfig(ForgeConfigSpec.Builder CLIENT_BUILDER) { 28 | CLIENT_BUILDER.comment("Settings for the mana system").push("mana"); 29 | 30 | MANA_HUD_X = CLIENT_BUILDER 31 | .comment("X location of the mana hud") 32 | .defineInRange("manaHudX", 10, -1, Integer.MAX_VALUE); 33 | MANA_HUD_Y = CLIENT_BUILDER 34 | .comment("Y location of the mana hud") 35 | .defineInRange("manaHudY", 10, -1, Integer.MAX_VALUE); 36 | MANA_HUD_COLOR = CLIENT_BUILDER 37 | .comment("Color of the mana hud") 38 | .defineInRange("manaHudColor", 0xffffffff, Integer.MIN_VALUE, Integer.MAX_VALUE); 39 | 40 | CLIENT_BUILDER.pop(); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/client/ClientManaData.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.client; 2 | 3 | /** 4 | * Class holding the data for mana client-side 5 | */ 6 | public class ClientManaData { 7 | 8 | private static int playerMana; 9 | private static int chunkMana; 10 | 11 | public static void set(int playerMana, int chunkMana) { 12 | ClientManaData.playerMana = playerMana; 13 | ClientManaData.chunkMana = chunkMana; 14 | } 15 | 16 | public static int getPlayerMana() { 17 | return playerMana; 18 | } 19 | 20 | public static int getChunkMana() { 21 | return chunkMana; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/client/KeyBindings.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.client; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import net.minecraft.client.KeyMapping; 5 | import net.minecraftforge.client.ClientRegistry; 6 | import net.minecraftforge.client.settings.KeyConflictContext; 7 | 8 | 9 | public class KeyBindings { 10 | 11 | public static final String KEY_CATEGORIES_TUTORIAL = "key.categories.tutorial"; 12 | public static final String KEY_GATHER_MANA = "key.gatherMana"; 13 | 14 | public static KeyMapping gatherManaKeyMapping; 15 | 16 | public static void init() { 17 | gatherManaKeyMapping = new KeyMapping(KEY_GATHER_MANA, KeyConflictContext.IN_GAME, InputConstants.getKey("key.keyboard.period"), KEY_CATEGORIES_TUTORIAL); 18 | ClientRegistry.registerKeyBinding(gatherManaKeyMapping); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/client/KeyInputHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.client; 2 | 3 | import com.example.tutorialv3.manasystem.network.PacketGatherMana; 4 | import com.example.tutorialv3.setup.Messages; 5 | import net.minecraftforge.client.event.InputEvent; 6 | 7 | public class KeyInputHandler { 8 | 9 | public static void onKeyInput(InputEvent.KeyInputEvent event) { 10 | if (KeyBindings.gatherManaKeyMapping.consumeClick()) { 11 | Messages.sendToServer(new PacketGatherMana()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/client/ManaOverlay.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.client; 2 | 3 | import com.example.tutorialv3.manasystem.ManaConfig; 4 | import net.minecraftforge.client.gui.IIngameOverlay; 5 | 6 | public class ManaOverlay { 7 | 8 | public static final IIngameOverlay HUD_MANA = (gui, poseStack, partialTicks, width, height) -> { 9 | String toDisplay = ClientManaData.getPlayerMana() + " / " + ClientManaData.getChunkMana(); 10 | int x = ManaConfig.MANA_HUD_X.get(); 11 | int y = ManaConfig.MANA_HUD_Y.get(); 12 | if (x >= 0 && y >= 0) { 13 | gui.getFont().draw(poseStack, toDisplay, x, y, ManaConfig.MANA_HUD_COLOR.get()); 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/data/Mana.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.data; 2 | 3 | public class Mana { 4 | private int mana; 5 | 6 | public Mana(int mana) { 7 | this.mana = mana; 8 | } 9 | 10 | public int getMana() { 11 | return mana; 12 | } 13 | 14 | public void setMana(int mana) { 15 | this.mana = mana; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/data/ManaEvents.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.data; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import net.minecraft.resources.ResourceLocation; 5 | import net.minecraft.world.entity.Entity; 6 | import net.minecraft.world.entity.player.Player; 7 | import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; 8 | import net.minecraftforge.event.AttachCapabilitiesEvent; 9 | import net.minecraftforge.event.TickEvent; 10 | import net.minecraftforge.event.entity.player.PlayerEvent; 11 | 12 | public class ManaEvents { 13 | 14 | public static void onAttachCapabilitiesPlayer(AttachCapabilitiesEvent event){ 15 | if (event.getObject() instanceof Player) { 16 | if (!event.getObject().getCapability(PlayerManaProvider.PLAYER_MANA).isPresent()) { 17 | event.addCapability(new ResourceLocation(TutorialV3.MODID, "playermana"), new PlayerManaProvider()); 18 | } 19 | } 20 | } 21 | 22 | public static void onPlayerCloned(PlayerEvent.Clone event) { 23 | if (event.isWasDeath()) { 24 | // We need to copyFrom the capabilities 25 | event.getOriginal().getCapability(PlayerManaProvider.PLAYER_MANA).ifPresent(oldStore -> { 26 | event.getPlayer().getCapability(PlayerManaProvider.PLAYER_MANA).ifPresent(newStore -> { 27 | newStore.copyFrom(oldStore); 28 | }); 29 | }); 30 | } 31 | } 32 | 33 | public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { 34 | event.register(PlayerMana.class); 35 | } 36 | 37 | public static void onWorldTick(TickEvent.WorldTickEvent event) { 38 | // Don't do anything client side 39 | if (event.world.isClientSide) { 40 | return; 41 | } 42 | if (event.phase == TickEvent.Phase.START) { 43 | return; 44 | } 45 | ManaManager manager = ManaManager.get(event.world); 46 | manager.tick(event.world); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/data/ManaManager.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.data; 2 | 3 | import com.example.tutorialv3.manasystem.ManaConfig; 4 | import com.example.tutorialv3.manasystem.network.PacketSyncManaToClient; 5 | import com.example.tutorialv3.setup.Messages; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.nbt.CompoundTag; 8 | import net.minecraft.nbt.ListTag; 9 | import net.minecraft.nbt.Tag; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.server.level.ServerPlayer; 12 | import net.minecraft.world.level.ChunkPos; 13 | import net.minecraft.world.level.Level; 14 | import net.minecraft.world.level.saveddata.SavedData; 15 | import net.minecraft.world.level.storage.DimensionDataStorage; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import javax.annotation.Nonnull; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.Random; 22 | 23 | public class ManaManager extends SavedData { 24 | 25 | private final Map manaMap = new HashMap<>(); 26 | private final Random random = new Random(); 27 | 28 | private int counter = 0; 29 | 30 | @Nonnull 31 | public static ManaManager get(Level level) { 32 | if (level.isClientSide) { 33 | throw new RuntimeException("Don't access this client-side!"); 34 | } 35 | DimensionDataStorage storage = ((ServerLevel)level).getDataStorage(); 36 | return storage.computeIfAbsent(ManaManager::new, ManaManager::new, "manamanager"); 37 | } 38 | 39 | @NotNull 40 | private Mana getManaInternal(BlockPos pos) { 41 | ChunkPos chunkPos = new ChunkPos(pos); 42 | return manaMap.computeIfAbsent(chunkPos, cp -> new Mana(random.nextInt(ManaConfig.CHUNK_MAX_MANA.get()) + ManaConfig.CHUNK_MIN_MANA.get())); 43 | } 44 | 45 | public int getMana(BlockPos pos) { 46 | Mana mana = getManaInternal(pos); 47 | return mana.getMana(); 48 | } 49 | 50 | public int extractMana(BlockPos pos) { 51 | Mana mana = getManaInternal(pos); 52 | int present = mana.getMana(); 53 | if (present > 0) { 54 | mana.setMana(present-1); 55 | setDirty(); 56 | return 1; 57 | } else { 58 | return 0; 59 | } 60 | } 61 | 62 | public void tick(Level level) { 63 | counter--; 64 | if (counter <= 0) { 65 | counter = 10; 66 | // Synchronize the mana to the players in this world 67 | // todo expansion: keep the previous data that was sent to the player and only send if changed 68 | level.players().forEach(player -> { 69 | if (player instanceof ServerPlayer serverPlayer) { 70 | int playerMana = serverPlayer.getCapability(PlayerManaProvider.PLAYER_MANA) 71 | .map(PlayerMana::getMana) 72 | .orElse(-1); 73 | int chunkMana = getMana(serverPlayer.blockPosition()); 74 | Messages.sendToPlayer(new PacketSyncManaToClient(playerMana, chunkMana), serverPlayer); 75 | } 76 | }); 77 | 78 | // todo expansion: here it would be possible to slowly regenerate mana in chunks 79 | } 80 | } 81 | 82 | public ManaManager() { 83 | } 84 | 85 | public ManaManager(CompoundTag tag) { 86 | ListTag list = tag.getList("mana", Tag.TAG_COMPOUND); 87 | for (Tag t : list) { 88 | CompoundTag manaTag = (CompoundTag) t; 89 | Mana mana = new Mana(manaTag.getInt("mana")); 90 | ChunkPos pos = new ChunkPos(manaTag.getInt("x"), manaTag.getInt("z")); 91 | manaMap.put(pos, mana); 92 | } 93 | } 94 | 95 | @Override 96 | public CompoundTag save(CompoundTag tag) { 97 | ListTag list = new ListTag(); 98 | manaMap.forEach((chunkPos, mana) -> { 99 | CompoundTag manaTag = new CompoundTag(); 100 | manaTag.putInt("x", chunkPos.x); 101 | manaTag.putInt("z", chunkPos.z); 102 | manaTag.putInt("mana", mana.getMana()); 103 | list.add(manaTag); 104 | }); 105 | tag.put("mana", list); 106 | return tag; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/data/PlayerMana.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.data; 2 | 3 | import net.minecraft.nbt.CompoundTag; 4 | 5 | public class PlayerMana { 6 | 7 | private int mana; 8 | 9 | public int getMana() { 10 | return mana; 11 | } 12 | 13 | public void setMana(int mana) { 14 | this.mana = mana; 15 | } 16 | 17 | public void addMana(int mana) { 18 | this.mana += mana; 19 | } 20 | 21 | public void copyFrom(PlayerMana source) { 22 | mana = source.mana; 23 | } 24 | 25 | 26 | public void saveNBTData(CompoundTag compound) { 27 | compound.putInt("mana", mana); 28 | } 29 | 30 | public void loadNBTData(CompoundTag compound) { 31 | mana = compound.getInt("mana"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/data/PlayerManaProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.data; 2 | 3 | import net.minecraft.core.Direction; 4 | import net.minecraft.nbt.CompoundTag; 5 | import net.minecraftforge.common.capabilities.Capability; 6 | import net.minecraftforge.common.capabilities.CapabilityManager; 7 | import net.minecraftforge.common.capabilities.CapabilityToken; 8 | import net.minecraftforge.common.capabilities.ICapabilityProvider; 9 | import net.minecraftforge.common.util.INBTSerializable; 10 | import net.minecraftforge.common.util.LazyOptional; 11 | 12 | import javax.annotation.Nonnull; 13 | import javax.annotation.Nullable; 14 | 15 | public class PlayerManaProvider implements ICapabilityProvider, INBTSerializable { 16 | 17 | public static Capability PLAYER_MANA = CapabilityManager.get(new CapabilityToken<>(){}); 18 | 19 | private PlayerMana playerMana = null; 20 | private final LazyOptional opt = LazyOptional.of(this::createPlayerMana); 21 | 22 | @Nonnull 23 | private PlayerMana createPlayerMana() { 24 | if (playerMana == null) { 25 | playerMana = new PlayerMana(); 26 | } 27 | return playerMana; 28 | } 29 | 30 | @Nonnull 31 | @Override 32 | public LazyOptional getCapability(@Nonnull Capability cap) { 33 | if (cap == PLAYER_MANA) { 34 | return opt.cast(); 35 | } 36 | return LazyOptional.empty(); 37 | } 38 | 39 | @Nonnull 40 | @Override 41 | public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { 42 | return getCapability(cap); 43 | } 44 | 45 | @Override 46 | public CompoundTag serializeNBT() { 47 | CompoundTag nbt = new CompoundTag(); 48 | createPlayerMana().saveNBTData(nbt); 49 | return nbt; 50 | } 51 | 52 | @Override 53 | public void deserializeNBT(CompoundTag nbt) { 54 | createPlayerMana().loadNBTData(nbt); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/network/PacketGatherMana.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.network; 2 | 3 | import com.example.tutorialv3.manasystem.data.ManaManager; 4 | import com.example.tutorialv3.manasystem.data.PlayerManaProvider; 5 | import net.minecraft.ChatFormatting; 6 | import net.minecraft.Util; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.chat.TranslatableComponent; 9 | import net.minecraft.server.level.ServerPlayer; 10 | import net.minecraftforge.network.NetworkEvent; 11 | 12 | import java.util.function.Supplier; 13 | 14 | public class PacketGatherMana { 15 | 16 | public static final String MESSAGE_NO_MANA = "message.nomana"; 17 | 18 | public PacketGatherMana() { 19 | } 20 | 21 | public PacketGatherMana(FriendlyByteBuf buf) { 22 | } 23 | 24 | public void toBytes(FriendlyByteBuf buf) { 25 | } 26 | 27 | public boolean handle(Supplier supplier) { 28 | NetworkEvent.Context ctx = supplier.get(); 29 | ctx.enqueueWork(() -> { 30 | // Here we are server side 31 | ServerPlayer player = ctx.getSender(); 32 | int extracted = ManaManager.get(player.level).extractMana(player.blockPosition()); 33 | if (extracted <= 0) { 34 | player.sendMessage(new TranslatableComponent(MESSAGE_NO_MANA).withStyle(ChatFormatting.RED), Util.NIL_UUID); 35 | } else { 36 | player.getCapability(PlayerManaProvider.PLAYER_MANA).ifPresent(playerMana -> { 37 | playerMana.addMana(extracted); 38 | }); 39 | } 40 | }); 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/manasystem/network/PacketSyncManaToClient.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.manasystem.network; 2 | 3 | import com.example.tutorialv3.manasystem.client.ClientManaData; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.network.NetworkEvent; 6 | 7 | import java.util.function.Supplier; 8 | 9 | public class PacketSyncManaToClient { 10 | 11 | private final int playerMana; 12 | private final int chunkMana; 13 | 14 | public PacketSyncManaToClient(int playerMana, int chunkMana) { 15 | this.playerMana = playerMana; 16 | this.chunkMana = chunkMana; 17 | } 18 | 19 | public PacketSyncManaToClient(FriendlyByteBuf buf) { 20 | playerMana = buf.readInt(); 21 | chunkMana = buf.readInt(); 22 | } 23 | 24 | public void toBytes(FriendlyByteBuf buf) { 25 | buf.writeInt(playerMana); 26 | buf.writeInt(chunkMana); 27 | } 28 | 29 | public boolean handle(Supplier supplier) { 30 | NetworkEvent.Context ctx = supplier.get(); 31 | ctx.enqueueWork(() -> { 32 | // Here we are client side. 33 | // Be very careful not to access client-only classes here! (like Minecraft) because 34 | // this packet needs to be available server-side too 35 | ClientManaData.set(playerMana, chunkMana); 36 | }); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/setup/ClientSetup.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.setup; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.client.GeneratorModelLoader; 5 | import com.example.tutorialv3.client.PowergenRenderer; 6 | import com.example.tutorialv3.client.PowergenScreen; 7 | import com.example.tutorialv3.entities.ThiefModel; 8 | import com.example.tutorialv3.entities.ThiefRenderer; 9 | import com.example.tutorialv3.manasystem.client.KeyBindings; 10 | import com.example.tutorialv3.manasystem.client.KeyInputHandler; 11 | import com.example.tutorialv3.manasystem.client.ManaOverlay; 12 | import net.minecraft.client.gui.screens.MenuScreens; 13 | import net.minecraft.client.renderer.ItemBlockRenderTypes; 14 | import net.minecraft.client.renderer.RenderType; 15 | import net.minecraft.client.renderer.texture.TextureAtlas; 16 | import net.minecraftforge.api.distmarker.Dist; 17 | import net.minecraftforge.client.event.EntityRenderersEvent; 18 | import net.minecraftforge.client.event.ModelRegistryEvent; 19 | import net.minecraftforge.client.event.TextureStitchEvent; 20 | import net.minecraftforge.client.gui.OverlayRegistry; 21 | import net.minecraftforge.client.model.ModelLoaderRegistry; 22 | import net.minecraftforge.common.MinecraftForge; 23 | import net.minecraftforge.eventbus.api.SubscribeEvent; 24 | import net.minecraftforge.fml.common.Mod; 25 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 26 | 27 | import static net.minecraftforge.client.gui.ForgeIngameGui.HOTBAR_ELEMENT; 28 | 29 | @Mod.EventBusSubscriber(modid = TutorialV3.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) 30 | public class ClientSetup { 31 | 32 | public static void init(FMLClientSetupEvent event) { 33 | event.enqueueWork(() -> { 34 | MenuScreens.register(Registration.POWERGEN_CONTAINER.get(), PowergenScreen::new); 35 | ItemBlockRenderTypes.setRenderLayer(Registration.POWERGEN.get(), RenderType.translucent()); 36 | PowergenRenderer.register(); 37 | }); 38 | MinecraftForge.EVENT_BUS.addListener(KeyInputHandler::onKeyInput); 39 | KeyBindings.init(); 40 | OverlayRegistry.registerOverlayAbove(HOTBAR_ELEMENT, "name", ManaOverlay.HUD_MANA); 41 | } 42 | 43 | @SubscribeEvent 44 | public static void onModelRegistryEvent(ModelRegistryEvent event) { 45 | ModelLoaderRegistry.registerLoader(GeneratorModelLoader.GENERATOR_LOADER, new GeneratorModelLoader()); 46 | } 47 | 48 | @SubscribeEvent 49 | public static void onRegisterLayers(EntityRenderersEvent.RegisterLayerDefinitions event) { 50 | event.registerLayerDefinition(ThiefModel.THIEF_LAYER, ThiefModel::createBodyLayer); 51 | } 52 | 53 | @SubscribeEvent 54 | public static void onRegisterRenderer(EntityRenderersEvent.RegisterRenderers event) { 55 | event.registerEntityRenderer(Registration.THIEF.get(), ThiefRenderer::new); 56 | } 57 | 58 | @SubscribeEvent 59 | public static void onTextureStitch(TextureStitchEvent.Pre event) { 60 | if (!event.getAtlas().location().equals(TextureAtlas.LOCATION_BLOCKS)) { 61 | return; 62 | } 63 | event.addSprite(PowergenRenderer.HALO); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/setup/Config.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.setup; 2 | 3 | import com.example.tutorialv3.blocks.GeneratorConfig; 4 | import com.example.tutorialv3.blocks.PowergenConfig; 5 | import com.example.tutorialv3.manasystem.ManaConfig; 6 | import com.example.tutorialv3.worldgen.ores.OresConfig; 7 | import net.minecraftforge.common.ForgeConfigSpec; 8 | import net.minecraftforge.fml.ModLoadingContext; 9 | import net.minecraftforge.fml.config.ModConfig; 10 | 11 | public class Config { 12 | 13 | public static void register() { 14 | registerServerConfigs(); 15 | registerCommonConfigs(); 16 | registerClientConfigs(); 17 | } 18 | 19 | private static void registerClientConfigs() { 20 | ForgeConfigSpec.Builder CLIENT_BUILDER = new ForgeConfigSpec.Builder(); 21 | PowergenConfig.registerClientConfig(CLIENT_BUILDER); 22 | ManaConfig.registerClientConfig(CLIENT_BUILDER); 23 | ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, CLIENT_BUILDER.build()); 24 | } 25 | 26 | private static void registerCommonConfigs() { 27 | ForgeConfigSpec.Builder COMMON_BUILDER = new ForgeConfigSpec.Builder(); 28 | OresConfig.registerCommonConfig(COMMON_BUILDER); 29 | ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, COMMON_BUILDER.build()); 30 | } 31 | 32 | private static void registerServerConfigs() { 33 | ForgeConfigSpec.Builder SERVER_BUILDER = new ForgeConfigSpec.Builder(); 34 | GeneratorConfig.registerServerConfig(SERVER_BUILDER); 35 | PowergenConfig.registerServerConfig(SERVER_BUILDER); 36 | ManaConfig.registerServerConfig(SERVER_BUILDER); 37 | ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, SERVER_BUILDER.build()); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/setup/Messages.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.setup; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.manasystem.network.PacketGatherMana; 5 | import com.example.tutorialv3.manasystem.network.PacketSyncManaToClient; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraftforge.network.NetworkDirection; 9 | import net.minecraftforge.network.NetworkRegistry; 10 | import net.minecraftforge.network.PacketDistributor; 11 | import net.minecraftforge.network.simple.SimpleChannel; 12 | 13 | public class Messages { 14 | 15 | private static SimpleChannel INSTANCE; 16 | 17 | private static int packetId = 0; 18 | private static int id() { 19 | return packetId++; 20 | } 21 | 22 | public static void register() { 23 | SimpleChannel net = NetworkRegistry.ChannelBuilder 24 | .named(new ResourceLocation(TutorialV3.MODID, "messages")) 25 | .networkProtocolVersion(() -> "1.0") 26 | .clientAcceptedVersions(s -> true) 27 | .serverAcceptedVersions(s -> true) 28 | .simpleChannel(); 29 | 30 | INSTANCE = net; 31 | 32 | net.messageBuilder(PacketGatherMana.class, id(), NetworkDirection.PLAY_TO_SERVER) 33 | .decoder(PacketGatherMana::new) 34 | .encoder(PacketGatherMana::toBytes) 35 | .consumer(PacketGatherMana::handle) 36 | .add(); 37 | net.messageBuilder(PacketSyncManaToClient.class, id(), NetworkDirection.PLAY_TO_CLIENT) 38 | .decoder(PacketSyncManaToClient::new) 39 | .encoder(PacketSyncManaToClient::toBytes) 40 | .consumer(PacketSyncManaToClient::handle) 41 | .add(); 42 | } 43 | 44 | public static void sendToServer(MSG message) { 45 | INSTANCE.sendToServer(message); 46 | } 47 | 48 | public static void sendToPlayer(MSG message, ServerPlayer player) { 49 | INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), message); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/setup/ModSetup.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.setup; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.entities.ThiefEntity; 5 | import com.example.tutorialv3.manasystem.data.ManaEvents; 6 | import com.example.tutorialv3.worldgen.dimensions.Dimensions; 7 | import com.example.tutorialv3.worldgen.ores.Ores; 8 | import net.minecraft.world.entity.Entity; 9 | import net.minecraft.world.item.CreativeModeTab; 10 | import net.minecraft.world.item.ItemStack; 11 | import net.minecraft.world.item.Items; 12 | import net.minecraftforge.common.MinecraftForge; 13 | import net.minecraftforge.event.entity.EntityAttributeCreationEvent; 14 | import net.minecraftforge.eventbus.api.EventPriority; 15 | import net.minecraftforge.eventbus.api.IEventBus; 16 | import net.minecraftforge.eventbus.api.SubscribeEvent; 17 | import net.minecraftforge.fml.common.Mod; 18 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 19 | 20 | @Mod.EventBusSubscriber(modid = TutorialV3.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) 21 | public class ModSetup { 22 | 23 | public static final String TAB_NAME = "tutorialv3"; 24 | 25 | public static final CreativeModeTab ITEM_GROUP = new CreativeModeTab(TAB_NAME) { 26 | @Override 27 | public ItemStack makeIcon() { 28 | return new ItemStack(Items.DIAMOND); 29 | } 30 | }; 31 | 32 | public static void setup() { 33 | IEventBus bus = MinecraftForge.EVENT_BUS; 34 | bus.addListener(Ores::onBiomeLoadingEvent); 35 | bus.addGenericListener(Entity.class, ManaEvents::onAttachCapabilitiesPlayer); 36 | bus.addListener(ManaEvents::onPlayerCloned); 37 | bus.addListener(ManaEvents::onRegisterCapabilities); 38 | bus.addListener(ManaEvents::onWorldTick); 39 | } 40 | 41 | public static void init(FMLCommonSetupEvent event) { 42 | event.enqueueWork(() -> { 43 | Ores.registerConfiguredFeatures(); 44 | Dimensions.register(); 45 | }); 46 | Messages.register(); 47 | } 48 | 49 | @SubscribeEvent 50 | public static void onAttributeCreate(EntityAttributeCreationEvent event) { 51 | event.put(Registration.THIEF.get(), ThiefEntity.prepareAttributes().build()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/setup/Registration.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.setup; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import com.example.tutorialv3.blocks.*; 5 | import com.example.tutorialv3.entities.ThiefEntity; 6 | import com.example.tutorialv3.worldgen.structures.PortalStructure; 7 | import com.example.tutorialv3.worldgen.structures.ThiefDenStructure; 8 | import net.minecraft.core.Registry; 9 | import net.minecraft.resources.ResourceLocation; 10 | import net.minecraft.tags.TagKey; 11 | import net.minecraft.world.entity.EntityType; 12 | import net.minecraft.world.entity.MobCategory; 13 | import net.minecraft.world.inventory.MenuType; 14 | import net.minecraft.world.item.BlockItem; 15 | import net.minecraft.world.item.Item; 16 | import net.minecraft.world.level.biome.Biome; 17 | import net.minecraft.world.level.block.Block; 18 | import net.minecraft.world.level.block.entity.BlockEntityType; 19 | import net.minecraft.world.level.block.state.BlockBehaviour; 20 | import net.minecraft.world.level.levelgen.feature.StructureFeature; 21 | import net.minecraft.world.level.levelgen.feature.configurations.JigsawConfiguration; 22 | import net.minecraft.world.level.levelgen.structure.StructureSet; 23 | import net.minecraft.world.level.material.Material; 24 | import net.minecraftforge.common.ForgeSpawnEggItem; 25 | import net.minecraftforge.common.extensions.IForgeMenuType; 26 | import net.minecraftforge.eventbus.api.IEventBus; 27 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 28 | import net.minecraftforge.registries.DeferredRegister; 29 | import net.minecraftforge.registries.ForgeRegistries; 30 | import net.minecraftforge.registries.RegistryObject; 31 | 32 | import static com.example.tutorialv3.TutorialV3.MODID; 33 | 34 | public class Registration { 35 | 36 | private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); 37 | private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); 38 | private static final DeferredRegister> BLOCK_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITIES, MODID); 39 | private static final DeferredRegister> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS, MODID); 40 | private static final DeferredRegister> ENTITIES = DeferredRegister.create(ForgeRegistries.ENTITIES, MODID); 41 | private static final DeferredRegister> STRUCTURES = DeferredRegister.create(ForgeRegistries.STRUCTURE_FEATURES, MODID); 42 | 43 | public static void init() { 44 | IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus(); 45 | BLOCKS.register(bus); 46 | ITEMS.register(bus); 47 | BLOCK_ENTITIES.register(bus); 48 | CONTAINERS.register(bus); 49 | ENTITIES.register(bus); 50 | STRUCTURES.register(bus); 51 | } 52 | 53 | // Some common properties for our blocks and items 54 | public static final BlockBehaviour.Properties BLOCK_PROPERTIES = BlockBehaviour.Properties.of(Material.STONE).strength(2f).requiresCorrectToolForDrops(); 55 | public static final Item.Properties ITEM_PROPERTIES = new Item.Properties().tab(ModSetup.ITEM_GROUP); 56 | 57 | public static final RegistryObject MYSTERIOUS_ORE_OVERWORLD = BLOCKS.register("mysterious_ore_overworld", () -> new Block(BLOCK_PROPERTIES)); 58 | public static final RegistryObject MYSTERIOUS_ORE_OVERWORLD_ITEM = fromBlock(MYSTERIOUS_ORE_OVERWORLD); 59 | public static final RegistryObject MYSTERIOUS_ORE_NETHER = BLOCKS.register("mysterious_ore_nether", () -> new Block(BLOCK_PROPERTIES)); 60 | public static final RegistryObject MYSTERIOUS_ORE_NETHER_ITEM = fromBlock(MYSTERIOUS_ORE_NETHER); 61 | public static final RegistryObject MYSTERIOUS_ORE_END = BLOCKS.register("mysterious_ore_end", () -> new Block(BLOCK_PROPERTIES)); 62 | public static final RegistryObject MYSTERIOUS_ORE_END_ITEM = fromBlock(MYSTERIOUS_ORE_END); 63 | public static final RegistryObject MYSTERIOUS_ORE_DEEPSLATE = BLOCKS.register("mysterious_ore_deepslate", () -> new Block(BLOCK_PROPERTIES)); 64 | public static final RegistryObject MYSTERIOUS_ORE_DEEPSLATE_ITEM = fromBlock(MYSTERIOUS_ORE_DEEPSLATE); 65 | 66 | public static final RegistryObject PORTAL_BLOCK = BLOCKS.register("portal", PortalBlock::new); 67 | public static final RegistryObject PORTAL_ITEM = fromBlock(PORTAL_BLOCK); 68 | 69 | public static final RegistryObject GENERATOR = BLOCKS.register("generator", GeneratorBlock::new); 70 | public static final RegistryObject GENERATOR_ITEM = fromBlock(GENERATOR); 71 | public static final RegistryObject> GENERATOR_BE = BLOCK_ENTITIES.register("generator", () -> BlockEntityType.Builder.of(GeneratorBE::new, GENERATOR.get()).build(null)); 72 | 73 | public static final RegistryObject POWERGEN = BLOCKS.register("powergen", PowergenBlock::new); 74 | public static final RegistryObject POWERGEN_ITEM = fromBlock(POWERGEN); 75 | public static final RegistryObject> POWERGEN_BE = BLOCK_ENTITIES.register("powergen", () -> BlockEntityType.Builder.of(PowergenBE::new, POWERGEN.get()).build(null)); 76 | public static final RegistryObject> POWERGEN_CONTAINER = CONTAINERS.register("powergen", 77 | () -> IForgeMenuType.create((windowId, inv, data) -> new PowergenContainer(windowId, data.readBlockPos(), inv, inv.player))); 78 | 79 | 80 | public static final RegistryObject RAW_MYSTERIOUS_CHUNK = ITEMS.register("raw_mysterious_chunk", () -> new Item(ITEM_PROPERTIES)); 81 | public static final RegistryObject MYSTERIOUS_INGOT = ITEMS.register("mysterious_ingot", () -> new Item(ITEM_PROPERTIES)); 82 | 83 | public static final TagKey MYSTERIOUS_ORE = TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(TutorialV3.MODID, "mysterious_ore")); 84 | public static final TagKey MYSTERIOUS_ORE_ITEM = TagKey.create(Registry.ITEM_REGISTRY, new ResourceLocation(TutorialV3.MODID, "mysterious_ore")); 85 | 86 | public static final RegistryObject> THIEF = ENTITIES.register("thief", () -> EntityType.Builder.of(ThiefEntity::new, MobCategory.CREATURE) 87 | .sized(0.6f, 1.95f) 88 | .clientTrackingRange(8) 89 | .setShouldReceiveVelocityUpdates(false) 90 | .build("thief")); 91 | public static final RegistryObject THIEF_EGG = ITEMS.register("thief", () -> new ForgeSpawnEggItem(THIEF, 0xff0000, 0x00ff00, ITEM_PROPERTIES)); 92 | 93 | public static final RegistryObject> THIEFDEN = STRUCTURES.register("thiefden", ThiefDenStructure::new); 94 | public static final RegistryObject> PORTAL = STRUCTURES.register("portal", PortalStructure::new); 95 | public static final ResourceLocation RL_MYSTERIOUS_DIMENSION_SET = new ResourceLocation(TutorialV3.MODID, "mysterious_dimension_structure_set"); 96 | 97 | public static final TagKey HAS_PORTAL = TagKey.create(Registry.BIOME_REGISTRY, new ResourceLocation(TutorialV3.MODID, "has_structure/portal")); 98 | public static final TagKey HAS_THIEFDEN = TagKey.create(Registry.BIOME_REGISTRY, new ResourceLocation(TutorialV3.MODID, "has_structure/thiefden")); 99 | public static final TagKey MYSTERIOUS_DIMENSION_STRUCTURE_SET = TagKey.create(Registry.STRUCTURE_SET_REGISTRY, RL_MYSTERIOUS_DIMENSION_SET); 100 | 101 | // Conveniance function: Take a RegistryObject and make a corresponding RegistryObject from it 102 | public static RegistryObject fromBlock(RegistryObject block) { 103 | return ITEMS.register(block.getId().getPath(), () -> new BlockItem(block.get(), ITEM_PROPERTIES)); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/varia/ClientTools.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.varia; 2 | 3 | import com.mojang.blaze3d.vertex.VertexFormatElement; 4 | import com.mojang.math.Transformation; 5 | import com.mojang.math.Vector3f; 6 | import com.mojang.math.Vector4f; 7 | import net.minecraft.client.renderer.block.model.BakedQuad; 8 | import net.minecraft.client.renderer.texture.TextureAtlasSprite; 9 | import net.minecraft.core.Direction; 10 | import net.minecraftforge.client.model.pipeline.BakedQuadBuilder; 11 | 12 | public class ClientTools { 13 | 14 | private static void putVertex(BakedQuadBuilder builder, Vector3f normal, Vector4f vector, 15 | float u, float v, TextureAtlasSprite sprite) { 16 | 17 | var elements = builder.getVertexFormat().getElements().asList(); 18 | for (int j = 0 ; j < elements.size() ; j++) { 19 | var e = elements.get(j); 20 | switch (e.getUsage()) { 21 | case POSITION -> builder.put(j, vector.x(), vector.y(), vector.z(), 1.0f); 22 | case COLOR -> builder.put(j, 1.0f, 1.0f, 1.0f, 1.0f); 23 | case UV -> putVertexUV(builder, u, v, sprite, j, e); 24 | case NORMAL -> builder.put(j, normal.x(), normal.y(), normal.z()); 25 | default -> builder.put(j); 26 | } 27 | } 28 | } 29 | 30 | private static void putVertexUV(BakedQuadBuilder builder, float u, float v, TextureAtlasSprite sprite, int j, VertexFormatElement e) { 31 | switch (e.getIndex()) { 32 | case 0 -> builder.put(j, sprite.getU(u), sprite.getV(v)); 33 | case 2 -> builder.put(j, (short) 0, (short) 0); 34 | default -> builder.put(j); 35 | } 36 | } 37 | 38 | public static BakedQuad createQuad(Vector3f v1, Vector3f v2, Vector3f v3, Vector3f v4, Transformation rotation, TextureAtlasSprite sprite) { 39 | Vector3f normal = v3.copy(); 40 | normal.sub(v2); 41 | Vector3f temp = v1.copy(); 42 | temp.sub(v2); 43 | normal.cross(temp); 44 | normal.normalize(); 45 | 46 | int tw = sprite.getWidth(); 47 | int th = sprite.getHeight(); 48 | 49 | rotation = rotation.blockCenterToCorner(); 50 | rotation.transformNormal(normal); 51 | 52 | Vector4f vv1 = new Vector4f(v1); rotation.transformPosition(vv1); 53 | Vector4f vv2 = new Vector4f(v2); rotation.transformPosition(vv2); 54 | Vector4f vv3 = new Vector4f(v3); rotation.transformPosition(vv3); 55 | Vector4f vv4 = new Vector4f(v4); rotation.transformPosition(vv4); 56 | 57 | var builder = new BakedQuadBuilder(sprite); 58 | builder.setQuadOrientation(Direction.getNearest(normal.x(), normal.y(), normal.z())); 59 | putVertex(builder, normal, vv1, 0, 0, sprite); 60 | putVertex(builder, normal, vv2, 0, th, sprite); 61 | putVertex(builder, normal, vv3, tw, th, sprite); 62 | putVertex(builder, normal, vv4, tw, 0, sprite); 63 | return builder.build(); 64 | } 65 | 66 | public static Vector3f v(float x, float y, float z) { 67 | return new Vector3f(x, y, z); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/varia/CustomEnergyStorage.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.varia; 2 | 3 | import net.minecraftforge.energy.EnergyStorage; 4 | 5 | /** 6 | * A custom energy storage implementation that has a few more options and also 7 | * allows for notifying the parent BE when changes occur 8 | */ 9 | public class CustomEnergyStorage extends EnergyStorage { 10 | 11 | public CustomEnergyStorage(int capacity, int maxTransfer) { 12 | super(capacity, maxTransfer, 0); 13 | } 14 | 15 | // Override this to (for example) call setChanged() on your block entity 16 | protected void onEnergyChanged() { 17 | } 18 | 19 | @Override 20 | public int receiveEnergy(int maxReceive, boolean simulate) { 21 | int rc = super.receiveEnergy(maxReceive, simulate); 22 | if (rc > 0 && !simulate) { 23 | onEnergyChanged(); 24 | } 25 | return rc; 26 | } 27 | 28 | @Override 29 | public int extractEnergy(int maxExtract, boolean simulate) { 30 | int rc = super.extractEnergy(maxExtract, simulate); 31 | if (rc > 0 && !simulate) { 32 | onEnergyChanged(); 33 | } 34 | return rc; 35 | } 36 | 37 | public void setEnergy(int energy) { 38 | this.energy = energy; 39 | onEnergyChanged(); 40 | } 41 | 42 | public void addEnergy(int energy) { 43 | this.energy += energy; 44 | if (this.energy > getMaxEnergyStored()) { 45 | this.energy = getEnergyStored(); 46 | } 47 | onEnergyChanged(); 48 | } 49 | 50 | public void consumeEnergy(int energy) { 51 | this.energy -= energy; 52 | if (this.energy < 0) { 53 | this.energy = 0; 54 | } 55 | onEnergyChanged(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/varia/Tools.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.varia; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.server.level.ServerLevel; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.item.ItemEntity; 8 | import net.minecraft.world.item.ItemStack; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.levelgen.Heightmap; 11 | import net.minecraftforge.common.util.ITeleporter; 12 | 13 | import java.util.function.Function; 14 | 15 | public class Tools { 16 | 17 | public static void spawnInWorld(Level level, BlockPos pos, ItemStack remaining) { 18 | if (!remaining.isEmpty()) { 19 | ItemEntity entityitem = new ItemEntity(level, pos.getX() + .5, pos.getY() + .5, pos.getZ() + .5, remaining); 20 | entityitem.setPickUpDelay(40); 21 | entityitem.setDeltaMovement(entityitem.getDeltaMovement().multiply(0, 1, 0)); 22 | level.addFreshEntity(entityitem); 23 | } 24 | } 25 | 26 | public static void teleport(ServerPlayer entity, ServerLevel destination, BlockPos pos, boolean findTop) { 27 | entity.changeDimension(destination, new ITeleporter() { 28 | @Override 29 | public Entity placeEntity(Entity entity, ServerLevel currentWorld, ServerLevel destWorld, float yaw, Function repositionEntity) { 30 | entity = repositionEntity.apply(false); 31 | int y = pos.getY(); 32 | if (findTop) { 33 | y = destination.getHeight(Heightmap.Types.WORLD_SURFACE_WG, pos.getX(), pos.getZ()); 34 | } 35 | entity.teleportTo(pos.getX(), y, pos.getZ()); 36 | return entity; 37 | } 38 | }); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/dimensions/Dimensions.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.dimensions; 2 | 3 | import com.example.tutorialv3.TutorialV3; 4 | import net.minecraft.core.Registry; 5 | import net.minecraft.resources.ResourceKey; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.world.level.Level; 8 | 9 | public class Dimensions { 10 | 11 | public static final ResourceKey MYSTERIOUS = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(TutorialV3.MODID, "mysterious")); 12 | 13 | public static void register() { 14 | Registry.register(Registry.CHUNK_GENERATOR, new ResourceLocation(TutorialV3.MODID, "mysterious_chunkgen"), 15 | MysteriousChunkGenerator.CODEC); 16 | Registry.register(Registry.BIOME_SOURCE, new ResourceLocation(TutorialV3.MODID, "biomes"), 17 | MysteriousBiomeProvider.CODEC); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/dimensions/MysteriousBiomeProvider.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.dimensions; 2 | 3 | import com.mojang.serialization.Codec; 4 | import net.minecraft.core.Holder; 5 | import net.minecraft.core.Registry; 6 | import net.minecraft.data.BuiltinRegistries; 7 | import net.minecraft.resources.RegistryOps; 8 | import net.minecraft.resources.ResourceKey; 9 | import net.minecraft.world.level.biome.Biome; 10 | import net.minecraft.world.level.biome.BiomeSource; 11 | import net.minecraft.world.level.biome.Biomes; 12 | import net.minecraft.world.level.biome.Climate; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | public class MysteriousBiomeProvider extends BiomeSource { 19 | 20 | public static final Codec CODEC = RegistryOps.retrieveRegistry(Registry.BIOME_REGISTRY) 21 | .xmap(MysteriousBiomeProvider::new, MysteriousBiomeProvider::getBiomeRegistry).codec(); 22 | 23 | private final Holder biome; 24 | private final Registry biomeRegistry; 25 | private static final List> SPAWN = Collections.singletonList(Biomes.PLAINS); 26 | 27 | public MysteriousBiomeProvider(Registry biomeRegistry) { 28 | super(getStartBiomes(biomeRegistry)); 29 | this.biomeRegistry = biomeRegistry; 30 | biome = biomeRegistry.getHolderOrThrow(Biomes.PLAINS); 31 | } 32 | 33 | private static List> getStartBiomes(Registry registry) { 34 | return SPAWN.stream().map(s -> registry.getHolderOrThrow(ResourceKey.create(BuiltinRegistries.BIOME.key(), s.location()))).collect(Collectors.toList()); 35 | } 36 | 37 | public Registry getBiomeRegistry() { 38 | return biomeRegistry; 39 | } 40 | 41 | @Override 42 | protected Codec codec() { 43 | return CODEC; 44 | } 45 | 46 | @Override 47 | public BiomeSource withSeed(long seed) { 48 | return this; 49 | } 50 | 51 | @Override 52 | public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { 53 | return biome; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/ores/DimensionBiomeFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.ores; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.core.Holder; 5 | import net.minecraft.resources.ResourceKey; 6 | import net.minecraft.world.level.Level; 7 | import net.minecraft.world.level.biome.Biome; 8 | import net.minecraft.world.level.levelgen.placement.PlacedFeature; 9 | import net.minecraft.world.level.levelgen.placement.PlacementContext; 10 | import net.minecraft.world.level.levelgen.placement.PlacementFilter; 11 | import net.minecraft.world.level.levelgen.placement.PlacementModifierType; 12 | 13 | import java.util.Random; 14 | import java.util.function.Predicate; 15 | 16 | /** 17 | * A biome filter that also checks if the dimension is right 18 | */ 19 | public class DimensionBiomeFilter extends PlacementFilter { 20 | 21 | private final Predicate> levelTest; 22 | 23 | public DimensionBiomeFilter(Predicate> levelTest) { 24 | this.levelTest = levelTest; 25 | } 26 | 27 | @Override 28 | protected boolean shouldPlace(PlacementContext context, Random random, BlockPos pos) { 29 | if (levelTest.test(context.getLevel().getLevel().dimension())) { 30 | PlacedFeature placedfeature = context.topFeature().orElseThrow(() -> new IllegalStateException("Tried to biome check an unregistered feature")); 31 | Holder biome = context.getLevel().getBiome(pos); 32 | return biome.value().getGenerationSettings().hasFeature(placedfeature); 33 | } else { 34 | return false; 35 | } 36 | } 37 | 38 | @Override 39 | public PlacementModifierType type() { 40 | return PlacementModifierType.BIOME_FILTER; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/ores/Ores.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.ores; 2 | 3 | import com.example.tutorialv3.setup.Registration; 4 | import com.example.tutorialv3.worldgen.dimensions.Dimensions; 5 | import net.minecraft.core.Holder; 6 | import net.minecraft.data.worldgen.features.OreFeatures; 7 | import net.minecraft.data.worldgen.placement.PlacementUtils; 8 | import net.minecraft.world.level.biome.Biome; 9 | import net.minecraft.world.level.levelgen.GenerationStep; 10 | import net.minecraft.world.level.levelgen.VerticalAnchor; 11 | import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; 12 | import net.minecraft.world.level.levelgen.feature.Feature; 13 | import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; 14 | import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration; 15 | import net.minecraft.world.level.levelgen.placement.*; 16 | import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTest; 17 | import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest; 18 | import net.minecraftforge.common.Tags; 19 | import net.minecraftforge.event.world.BiomeLoadingEvent; 20 | 21 | public class Ores { 22 | 23 | public static final RuleTest IN_ENDSTONE = new TagMatchTest(Tags.Blocks.END_STONES); 24 | 25 | public static Holder MYSTERIOUS_OREGEN; 26 | public static Holder OVERWORLD_OREGEN; 27 | public static Holder DEEPSLATE_OREGEN; 28 | public static Holder NETHER_OREGEN; 29 | public static Holder END_OREGEN; 30 | 31 | public static void registerConfiguredFeatures() { 32 | OreConfiguration mysteriousConfig = new OreConfiguration(OreFeatures.STONE_ORE_REPLACEABLES, Registration.MYSTERIOUS_ORE_OVERWORLD.get().defaultBlockState(), OresConfig.MYSTERIOUS_VEINSIZE.get()); 33 | MYSTERIOUS_OREGEN = registerPlacedFeature("mysterious_mysterious_ore", new ConfiguredFeature<>(Feature.ORE, mysteriousConfig), 34 | CountPlacement.of(OresConfig.MYSTERIOUS_AMOUNT.get()), 35 | InSquarePlacement.spread(), 36 | new DimensionBiomeFilter(key -> key.equals(Dimensions.MYSTERIOUS)), 37 | HeightRangePlacement.uniform(VerticalAnchor.absolute(0), VerticalAnchor.absolute(90))); 38 | 39 | OreConfiguration overworldConfig = new OreConfiguration(OreFeatures.STONE_ORE_REPLACEABLES, Registration.MYSTERIOUS_ORE_OVERWORLD.get().defaultBlockState(), OresConfig.OVERWORLD_VEINSIZE.get()); 40 | OVERWORLD_OREGEN = registerPlacedFeature("overworld_mysterious_ore", new ConfiguredFeature<>(Feature.ORE, overworldConfig), 41 | CountPlacement.of(OresConfig.OVERWORLD_AMOUNT.get()), 42 | InSquarePlacement.spread(), 43 | new DimensionBiomeFilter(key -> !Dimensions.MYSTERIOUS.equals(key)), 44 | HeightRangePlacement.uniform(VerticalAnchor.absolute(0), VerticalAnchor.absolute(90))); 45 | 46 | OreConfiguration deepslateConfig = new OreConfiguration(OreFeatures.DEEPSLATE_ORE_REPLACEABLES, Registration.MYSTERIOUS_ORE_DEEPSLATE.get().defaultBlockState(), OresConfig.DEEPSLATE_VEINSIZE.get()); 47 | DEEPSLATE_OREGEN = registerPlacedFeature("deepslate_mysterious_ore", new ConfiguredFeature<>(Feature.ORE, deepslateConfig), 48 | CountPlacement.of(OresConfig.DEEPSLATE_AMOUNT.get()), 49 | InSquarePlacement.spread(), 50 | BiomeFilter.biome(), 51 | HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(64))); 52 | 53 | OreConfiguration netherConfig = new OreConfiguration(OreFeatures.NETHER_ORE_REPLACEABLES, Registration.MYSTERIOUS_ORE_NETHER.get().defaultBlockState(), OresConfig.NETHER_VEINSIZE.get()); 54 | NETHER_OREGEN = registerPlacedFeature("nether_mysterious_ore", new ConfiguredFeature<>(Feature.ORE, netherConfig), 55 | CountPlacement.of(OresConfig.NETHER_AMOUNT.get()), 56 | InSquarePlacement.spread(), 57 | BiomeFilter.biome(), 58 | HeightRangePlacement.uniform(VerticalAnchor.absolute(0), VerticalAnchor.absolute(90))); 59 | 60 | OreConfiguration endConfig = new OreConfiguration(IN_ENDSTONE, Registration.MYSTERIOUS_ORE_END.get().defaultBlockState(), OresConfig.END_VEINSIZE.get()); 61 | END_OREGEN = registerPlacedFeature("end_mysterious_ore", new ConfiguredFeature<>(Feature.ORE, endConfig), 62 | CountPlacement.of(OresConfig.END_AMOUNT.get()), 63 | InSquarePlacement.spread(), 64 | BiomeFilter.biome(), 65 | HeightRangePlacement.uniform(VerticalAnchor.absolute(0), VerticalAnchor.absolute(100))); 66 | } 67 | 68 | private static > Holder registerPlacedFeature(String registryName, ConfiguredFeature feature, PlacementModifier... placementModifiers) { 69 | return PlacementUtils.register(registryName, Holder.direct(feature), placementModifiers); 70 | } 71 | 72 | public static void onBiomeLoadingEvent(BiomeLoadingEvent event) { 73 | if (event.getCategory() == Biome.BiomeCategory.NETHER) { 74 | event.getGeneration().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, NETHER_OREGEN); 75 | } else if (event.getCategory() == Biome.BiomeCategory.THEEND) { 76 | event.getGeneration().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, END_OREGEN); 77 | } else { 78 | event.getGeneration().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, MYSTERIOUS_OREGEN); 79 | event.getGeneration().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, OVERWORLD_OREGEN); 80 | event.getGeneration().addFeature(GenerationStep.Decoration.UNDERGROUND_ORES, DEEPSLATE_OREGEN); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/ores/OresConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.ores; 2 | 3 | import net.minecraftforge.common.ForgeConfigSpec; 4 | 5 | public class OresConfig { 6 | 7 | public static ForgeConfigSpec.IntValue MYSTERIOUS_VEINSIZE; 8 | public static ForgeConfigSpec.IntValue MYSTERIOUS_AMOUNT; 9 | public static ForgeConfigSpec.IntValue OVERWORLD_VEINSIZE; 10 | public static ForgeConfigSpec.IntValue OVERWORLD_AMOUNT; 11 | public static ForgeConfigSpec.IntValue DEEPSLATE_VEINSIZE; 12 | public static ForgeConfigSpec.IntValue DEEPSLATE_AMOUNT; 13 | public static ForgeConfigSpec.IntValue NETHER_VEINSIZE; 14 | public static ForgeConfigSpec.IntValue NETHER_AMOUNT; 15 | public static ForgeConfigSpec.IntValue END_VEINSIZE; 16 | public static ForgeConfigSpec.IntValue END_AMOUNT; 17 | 18 | public static void registerCommonConfig(ForgeConfigSpec.Builder COMMON_BUILDER) { 19 | COMMON_BUILDER.comment("Settings for ore generation").push("ores"); 20 | 21 | MYSTERIOUS_VEINSIZE = COMMON_BUILDER 22 | .comment("Veinsize of our ore in the mysterious dimension") 23 | .defineInRange("mysteriousVeinsize", 25, 1, Integer.MAX_VALUE); 24 | MYSTERIOUS_AMOUNT = COMMON_BUILDER 25 | .comment("Amount of veines of our ore in the mysterious dimension") 26 | .defineInRange("mysteriousAmount", 10, 1, Integer.MAX_VALUE); 27 | OVERWORLD_VEINSIZE = COMMON_BUILDER 28 | .comment("Veinsize of our ore in the overworld dimension") 29 | .defineInRange("overworldVeinsize", 5, 1, Integer.MAX_VALUE); 30 | OVERWORLD_AMOUNT = COMMON_BUILDER 31 | .comment("Amount of veines of our ore in the overworld dimension") 32 | .defineInRange("overworldAmount", 3, 1, Integer.MAX_VALUE); 33 | DEEPSLATE_VEINSIZE = COMMON_BUILDER 34 | .comment("Veinsize of our ore in the overworld dimension but for deepslate") 35 | .defineInRange("deepslateVeinsize", 5, 1, Integer.MAX_VALUE); 36 | DEEPSLATE_AMOUNT = COMMON_BUILDER 37 | .comment("Amount of veines of our ore in the overworld dimension but for deepslate") 38 | .defineInRange("deepslateAmount", 3, 1, Integer.MAX_VALUE); 39 | NETHER_VEINSIZE = COMMON_BUILDER 40 | .comment("Veinsize of our ore in the nether dimension") 41 | .defineInRange("netherVeinsize", 5, 1, Integer.MAX_VALUE); 42 | NETHER_AMOUNT = COMMON_BUILDER 43 | .comment("Amount of veines of our ore in the nether dimension") 44 | .defineInRange("netherAmount", 3, 1, Integer.MAX_VALUE); 45 | END_VEINSIZE = COMMON_BUILDER 46 | .comment("Veinsize of our ore in the end dimension") 47 | .defineInRange("endVeinsize", 10, 1, Integer.MAX_VALUE); 48 | END_AMOUNT = COMMON_BUILDER 49 | .comment("Amount of veines of our ore in the end dimension") 50 | .defineInRange("endAmount", 6, 1, Integer.MAX_VALUE); 51 | 52 | COMMON_BUILDER.pop(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/structures/PortalStructure.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.structures; 2 | 3 | import com.example.tutorialv3.worldgen.dimensions.MysteriousChunkGenerator; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.level.LevelHeightAccessor; 6 | import net.minecraft.world.level.NoiseColumn; 7 | import net.minecraft.world.level.levelgen.GenerationStep; 8 | import net.minecraft.world.level.levelgen.Heightmap; 9 | import net.minecraft.world.level.levelgen.LegacyRandomSource; 10 | import net.minecraft.world.level.levelgen.WorldgenRandom; 11 | import net.minecraft.world.level.levelgen.feature.StructureFeature; 12 | import net.minecraft.world.level.levelgen.feature.configurations.JigsawConfiguration; 13 | import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; 14 | import net.minecraft.world.level.levelgen.structure.PostPlacementProcessor; 15 | import net.minecraft.world.level.levelgen.structure.pieces.PieceGenerator; 16 | import net.minecraft.world.level.levelgen.structure.pieces.PieceGeneratorSupplier; 17 | import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import java.util.Optional; 21 | 22 | public class PortalStructure extends StructureFeature { 23 | 24 | public PortalStructure() { 25 | super(JigsawConfiguration.CODEC, context -> createPiecesGenerator(context), PostPlacementProcessor.NONE); 26 | } 27 | 28 | @Override 29 | public GenerationStep.Decoration step() { 30 | return GenerationStep.Decoration.UNDERGROUND_STRUCTURES; 31 | } 32 | 33 | private static Optional> createPiecesGenerator(PieceGeneratorSupplier.Context context) { 34 | boolean overworld = !(context.chunkGenerator() instanceof MysteriousChunkGenerator); 35 | 36 | // Turns the chunk coordinates into actual coordinates we can use. (center of that chunk) 37 | BlockPos blockpos = context.chunkPos().getMiddleBlockPosition(0); 38 | 39 | if (overworld) { 40 | // If we are generating for the overworld we want our portal to spawn underground. Preferably in an open area 41 | blockpos = findSuitableSpot(context, blockpos); 42 | } 43 | 44 | // Return the pieces generator that is now set up so that the game runs it when it needs to create the layout of structure pieces. 45 | return JigsawPlacement.addPieces( 46 | context, 47 | PoolElementStructurePiece::new, 48 | blockpos, 49 | false, 50 | !overworld 51 | ); 52 | } 53 | 54 | @NotNull 55 | private static BlockPos findSuitableSpot(PieceGeneratorSupplier.Context context, BlockPos blockpos) { 56 | LevelHeightAccessor heightAccessor = context.heightAccessor(); 57 | 58 | // Get the top y location that is solid 59 | int y = context.chunkGenerator().getBaseHeight(blockpos.getX(), blockpos.getZ(), Heightmap.Types.WORLD_SURFACE_WG, heightAccessor); 60 | 61 | // Create a randomgenerator that depends on the current chunk location. That way if the world is recreated 62 | // with the same seed the feature will end up at the same spot 63 | WorldgenRandom worldgenrandom = new WorldgenRandom(new LegacyRandomSource(context.seed())); 64 | worldgenrandom.setLargeFeatureSeed(context.seed(), context.chunkPos().x, context.chunkPos().z); 65 | 66 | // Pick a random y location between a low and a high point 67 | y = worldgenrandom.nextIntBetweenInclusive(heightAccessor.getMinBuildHeight()+20, y - 10); 68 | 69 | // Go down until we find a spot that has air. Then go down until we find a spot that is solid again 70 | NoiseColumn baseColumn = context.chunkGenerator().getBaseColumn(blockpos.getX(), blockpos.getZ(), heightAccessor); 71 | int yy = y; // Remember 'y' because we will just use this if we can't find an air bubble 72 | int lower = heightAccessor.getMinBuildHeight() + 3; // Lower limit, don't go below this 73 | while (yy > lower && !baseColumn.getBlock(yy).isAir()) { 74 | yy--; 75 | } 76 | // If we found air we go down until we find a non-air block 77 | if (yy > lower) { 78 | while (yy > lower && baseColumn.getBlock(yy).isAir()) { 79 | yy--; 80 | } 81 | if (yy > lower) { 82 | // We found a possible spawn spot 83 | y = yy + 1; 84 | } 85 | } 86 | 87 | return blockpos.atY(y); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/example/tutorialv3/worldgen/structures/ThiefDenStructure.java: -------------------------------------------------------------------------------- 1 | package com.example.tutorialv3.worldgen.structures; 2 | 3 | import net.minecraft.core.BlockPos; 4 | import net.minecraft.world.level.NoiseColumn; 5 | import net.minecraft.world.level.block.state.BlockState; 6 | import net.minecraft.world.level.levelgen.GenerationStep; 7 | import net.minecraft.world.level.levelgen.Heightmap; 8 | import net.minecraft.world.level.levelgen.feature.StructureFeature; 9 | import net.minecraft.world.level.levelgen.feature.configurations.JigsawConfiguration; 10 | import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; 11 | import net.minecraft.world.level.levelgen.structure.PostPlacementProcessor; 12 | import net.minecraft.world.level.levelgen.structure.pieces.PieceGenerator; 13 | import net.minecraft.world.level.levelgen.structure.pieces.PieceGeneratorSupplier; 14 | import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement; 15 | 16 | import java.util.Optional; 17 | 18 | public class ThiefDenStructure extends StructureFeature { 19 | 20 | public ThiefDenStructure() { 21 | super(JigsawConfiguration.CODEC, context -> { 22 | if (!isFeatureChunk(context)) { 23 | return Optional.empty(); 24 | } else { 25 | return createPiecesGenerator(context); 26 | } 27 | }, PostPlacementProcessor.NONE); 28 | } 29 | 30 | @Override 31 | public GenerationStep.Decoration step() { 32 | return GenerationStep.Decoration.SURFACE_STRUCTURES; 33 | } 34 | 35 | // Test if the current chunk (from context) has a valid location for our structure 36 | private static boolean isFeatureChunk(PieceGeneratorSupplier.Context context) { 37 | BlockPos pos = context.chunkPos().getWorldPosition(); 38 | 39 | // Get height of land (stops at first non-air block) 40 | int landHeight = context.chunkGenerator().getFirstOccupiedHeight(pos.getX(), pos.getZ(), Heightmap.Types.WORLD_SURFACE_WG, context.heightAccessor()); 41 | 42 | // Grabs column of blocks at given position. In overworld, this column will be made of stone, water, and air. 43 | // In nether, it will be netherrack, lava, and air. End will only be endstone and air. It depends on what block 44 | // the chunk generator will place for that dimension. 45 | NoiseColumn columnOfBlocks = context.chunkGenerator().getBaseColumn(pos.getX(), pos.getZ(), context.heightAccessor()); 46 | 47 | // Combine the column of blocks with land height and you get the top block itself which you can test. 48 | BlockState topBlock = columnOfBlocks.getBlock(landHeight); 49 | 50 | // Now we test to make sure our structure is not spawning on water or other fluids. 51 | // You can do height check instead too to make it spawn at high elevations. 52 | return topBlock.getFluidState().isEmpty(); //landHeight > 100; 53 | } 54 | 55 | private static Optional> createPiecesGenerator(PieceGeneratorSupplier.Context context) { 56 | // Turns the chunk coordinates into actual coordinates we can use. (center of that chunk) 57 | BlockPos blockpos = context.chunkPos().getMiddleBlockPosition(0); 58 | 59 | // Return the pieces generator that is now set up so that the game runs it when it needs to create the layout of structure pieces. 60 | return JigsawPlacement.addPieces( 61 | context, 62 | PoolElementStructurePiece::new, 63 | blockpos, 64 | false, 65 | true); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/META-INF/accesstransformer.cfg -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | # This is an example mods.toml file. It contains the data relating to the loading mods. 2 | # There are several mandatory fields (#mandatory), and many more that are optional (#optional). 3 | # The overall format is standard TOML format, v0.5.0. 4 | # Note that there are a couple of TOML lists in this file. 5 | # Find more information on toml format here: https://github.com/toml-lang/toml 6 | # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml 7 | modLoader="javafml" #mandatory 8 | # A version range to match for said mod loader - for regular FML @Mod it will be the forge version 9 | loaderVersion="[39,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. 10 | # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. 11 | # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. 12 | license="All rights reserved" 13 | # A URL to refer people to when problems occur with this mod 14 | #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional 15 | # A list of mods - how many allowed here is determined by the individual mod loader 16 | [[mods]] #mandatory 17 | # The modid of the mod 18 | modId="tutorialv3" #mandatory 19 | # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it 20 | # ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata 21 | # see the associated build.gradle script for how to populate this completely automatically during a build 22 | version="${file.jarVersion}" #mandatory 23 | # A display name for the mod 24 | displayName="Example Mod" #mandatory 25 | # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ 26 | #updateJSONURL="https://change.me.example.invalid/updates.json" #optional 27 | # A URL for the "homepage" for this mod, displayed in the mod UI 28 | #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional 29 | # A file name (in the root of the mod JAR) containing a logo for display 30 | logoFile="tutorialv3.png" #optional 31 | # A text field displayed in the mod UI 32 | credits="Thanks for this example mod goes to Java" #optional 33 | # A text field displayed in the mod UI 34 | authors="Love, Cheese and small house plants" #optional 35 | # The description text for the mod (multi line!) (#mandatory) 36 | description=''' 37 | This is a long form description of the mod. You can write whatever you want here 38 | 39 | Have some lorem ipsum. 40 | 41 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 42 | ''' 43 | # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. 44 | [[dependencies.tutorialv3]] #optional 45 | # the modid of the dependency 46 | modId="forge" #mandatory 47 | # Does this dependency have to exist - if not, ordering below must be specified 48 | mandatory=true #mandatory 49 | # The version range of the dependency 50 | versionRange="[39,)" #mandatory 51 | # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory 52 | ordering="NONE" 53 | # Side this dependency is applied on - BOTH, CLIENT or SERVER 54 | side="BOTH" 55 | # Here's another dependency 56 | [[dependencies.tutorialv3]] 57 | modId="minecraft" 58 | mandatory=true 59 | # This version range declares a minimum of the current minecraft version up to but not including the next major version 60 | versionRange="[1.18.1,1.19)" 61 | ordering="NONE" 62 | side="BOTH" 63 | -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/generator_front.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_front_powered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/generator_front_powered.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_front_powered.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 2 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/generator_off.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/generator_on.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/generator_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/generator_side.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_deepslate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_deepslate.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_end.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_end.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_nether.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_nether.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_overworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/mysterious_ore_overworld.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/portal_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/portal_side.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/portal_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/portal_top.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/portal_top.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 2 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/powergen_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/powergen_off.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/powergen_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/powergen_on.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/powergen_on.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 2 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/block/powergen_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/block/powergen_window.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/effect/halo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/effect/halo.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/entity/thief.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/entity/thief.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/gui/powergen_gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/gui/powergen_gui.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/item/mysterious_ingot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/item/mysterious_ingot.png -------------------------------------------------------------------------------- /src/main/resources/assets/tutorialv3/textures/item/raw_mysterious_chunk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/assets/tutorialv3/textures/item/raw_mysterious_chunk.png -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/dimension/mysterious.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "tutorialv3:mysterious", 3 | "generator": { 4 | "type": "tutorialv3:mysterious_chunkgen", 5 | "settings": { 6 | "base": 65, 7 | "verticalvariance": 10, 8 | "horizontalvariance": 8 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/dimension_type/mysterious.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mysterious Dimension", 3 | "ultrawarm": false, 4 | "natural": true, 5 | "coordinate_scale": 0.125, 6 | "height": 256, 7 | "min_y": 0, 8 | "shrunk": false, 9 | "has_skylight": true, 10 | "has_ceiling": false, 11 | "ambient_light": 0, 12 | "fixed_time": 1000, 13 | "piglin_safe": false, 14 | "bed_works": true, 15 | "respawn_anchor_works": true, 16 | "has_raids": false, 17 | "logical_height": 256, 18 | "infiniburn": "#minecraft:infiniburn_overworld", 19 | "effects": "minecraft:overworld" 20 | } -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/structures/portal.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/data/tutorialv3/structures/portal.nbt -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/structures/thiefden.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McJty/TutorialV3/270ff33667f209ba73d0d83d7cd4efcbb17b2c99/src/main/resources/data/tutorialv3/structures/thiefden.nbt -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/configured_structure_feature/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "tutorialv3:portal", 3 | "config": { 4 | "start_pool": "tutorialv3:portal/start_pool", 5 | "size": 2 6 | }, 7 | 8 | "biomes": "#tutorialv3:has_structure/portal", 9 | "adapt_noise": true, 10 | "spawn_overrides": { 11 | "monster": { 12 | "bounding_box": "piece", 13 | "spawns": [ 14 | { 15 | "type": "minecraft:illusioner", 16 | "weight": 1, 17 | "minCount": 1, 18 | "maxCount": 2 19 | }, 20 | { 21 | "type": "minecraft:pillager", 22 | "weight": 2, 23 | "minCount": 1, 24 | "maxCount": 4 25 | } 26 | ] 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/configured_structure_feature/thiefden.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "tutorialv3:thiefden", 3 | "config": { 4 | "start_pool": "tutorialv3:thiefden/start_pool", 5 | "size": 2 6 | }, 7 | 8 | "biomes": "#tutorialv3:has_structure/thiefden", 9 | "adapt_noise": true, 10 | "spawn_overrides": { 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/structure_set/portal.json: -------------------------------------------------------------------------------- 1 | { 2 | "structures": [ 3 | { 4 | "structure": "tutorialv3:portal", 5 | "weight": 1 6 | } 7 | ], 8 | "placement": { 9 | "salt": 1694767081, 10 | "spacing": 8, 11 | "separation": 4, 12 | "type": "minecraft:random_spread" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/structure_set/thiefden.json: -------------------------------------------------------------------------------- 1 | { 2 | "structures": [ 3 | { 4 | "structure": "tutorialv3:thiefden", 5 | "weight": 1 6 | } 7 | ], 8 | "placement": { 9 | "salt": 1594767081, 10 | "spacing": 8, 11 | "separation": 4, 12 | "type": "minecraft:random_spread" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/template_pool/portal/start_pool.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorialv3:portal/start_pool", 3 | "fallback": "minecraft:empty", 4 | 5 | "elements": [ 6 | { 7 | "weight": 1, 8 | "element": { 9 | "location": "tutorialv3:portal", 10 | "processors": "minecraft:empty", 11 | "projection": "rigid", 12 | "element_type": "minecraft:single_pool_element" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/main/resources/data/tutorialv3/worldgen/template_pool/thiefden/start_pool.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorialv3:thiefden/start_pool", 3 | "fallback": "minecraft:empty", 4 | 5 | "elements": [ 6 | { 7 | "weight": 1, 8 | "element": { 9 | "location": "tutorialv3:thiefden", 10 | "processors": "minecraft:empty", 11 | "projection": "rigid", 12 | "element_type": "minecraft:single_pool_element" 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "tutorialv3 resources", 4 | "pack_format": 8 5 | } 6 | } 7 | --------------------------------------------------------------------------------