├── src ├── main │ ├── resources │ │ ├── META-INF │ │ │ └── accesstransformer.cfg │ │ └── assets │ │ │ └── clickmachine │ │ │ ├── textures │ │ │ ├── blocks │ │ │ │ ├── front.png │ │ │ │ ├── side.png │ │ │ │ ├── front_off.png │ │ │ │ └── front.png.mcmeta │ │ │ └── gui │ │ │ │ └── auto_click.png │ │ │ ├── models │ │ │ ├── block │ │ │ │ ├── click_machine.json │ │ │ │ └── click_machine_off.json │ │ │ └── item │ │ │ │ └── click_machine.json │ │ │ ├── lang │ │ │ ├── es_ar.json │ │ │ ├── es_es.json │ │ │ ├── es_mx.json │ │ │ ├── ru_ru.json │ │ │ ├── it_it.json │ │ │ ├── tr_tr.json │ │ │ ├── en_us.json │ │ │ ├── zh_cn.json │ │ │ ├── de_de.json │ │ │ └── pt_br.json │ │ │ └── blockstates │ │ │ └── click_machine.json │ └── java │ │ └── dev │ │ └── shadowsoffire │ │ └── clickmachine │ │ ├── ClickMachineClient.java │ │ ├── data │ │ ├── ClickRecipes.java │ │ └── LootProvider.java │ │ ├── gui │ │ ├── ClickMachineMenu.java │ │ ├── SpeedSlider.java │ │ ├── ClickerCheckboxButton.java │ │ └── ClickMachineScreen.java │ │ ├── block │ │ ├── ClickMachineBlock.java │ │ └── ClickMachineTile.java │ │ ├── ClickMachine.java │ │ ├── ClickMachineConfig.java │ │ └── util │ │ └── FakePlayerUtil.java ├── templates │ ├── coremods.json │ ├── mixins.json │ └── neoforge.mods.toml └── generated │ └── resources │ ├── META-INF │ ├── coremods.json │ └── neoforge.mods.toml │ ├── .cache │ ├── 103d9f3f36b01595f1aa5172191e60eff02e6924 │ ├── 9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e │ └── 59eb3dbb5f86130e09b3c62d89b9525ee01cf52d │ ├── clickmachine.mixins.json │ └── data │ └── clickmachine │ ├── loot_table │ └── blocks │ │ └── click_machine.json │ └── recipe │ └── click_machine.json ├── gradlew ├── .github └── FUNDING.yml ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── libs └── Placebo-1.19.2-7.0.0-dev.jar ├── .gitignore ├── .gitattributes ├── gradle.properties ├── LICENSE ├── changelog.md └── gradlew.bat /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/templates/coremods.json: -------------------------------------------------------------------------------- 1 | { 2 | ${coremods} 3 | } -------------------------------------------------------------------------------- /src/generated/resources/META-INF/coremods.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/gradlew -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Shadows-of-Fire 2 | patreon: Shadows_of_Fire 3 | custom: https://paypal.me/ShadowsOfFire 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins { 3 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' 4 | } 5 | 6 | -------------------------------------------------------------------------------- /src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924: -------------------------------------------------------------------------------- 1 | // 1.21.1 2024-11-02T14:52:02.9822631 Registries 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /libs/Placebo-1.19.2-7.0.0-dev.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/libs/Placebo-1.19.2-7.0.0-dev.jar -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/textures/blocks/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/src/main/resources/assets/clickmachine/textures/blocks/front.png -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/textures/blocks/side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/src/main/resources/assets/clickmachine/textures/blocks/side.png -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/textures/gui/auto_click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/src/main/resources/assets/clickmachine/textures/gui/auto_click.png -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/textures/blocks/front_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shadows-of-Fire/ClickMachine/HEAD/src/main/resources/assets/clickmachine/textures/blocks/front_off.png -------------------------------------------------------------------------------- /src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e: -------------------------------------------------------------------------------- 1 | // 1.21.1 2024-11-02T14:52:02.9832628 Recipes 2 | f84f8da76fd60fd5ae2b952827c36d59db3c7439 data/clickmachine/recipe/click_machine.json 3 | -------------------------------------------------------------------------------- /src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d: -------------------------------------------------------------------------------- 1 | // 1.21.1 2024-11-02T14:52:02.9812632 Loot Tables 2 | 9b56f2b2dc9a402dd04299200a34d877c281b2b4 data/clickmachine/loot_table/blocks/click_machine.json 3 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/textures/blocks/front.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "interpolate": false, 4 | "width": 16, 5 | "height": 16, 6 | "frametime": 7, 7 | "frames": [ 8 | 0, 1, 2, 1 9 | ] 10 | } 11 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/generated/resources/clickmachine.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.shadowsoffire.clickmachine.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "mixins": [ 6 | 7 | ], 8 | "client": [ 9 | 10 | ], 11 | "minVersion": "0.8" 12 | } -------------------------------------------------------------------------------- /src/templates/mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "${modGroup}.${modid}.mixin", 4 | "compatibilityLevel": "JAVA_${javaVersion}", 5 | "mixins": [ 6 | ${mixins} 7 | ], 8 | "client": [ 9 | ${clientMixins} 10 | ], 11 | "minVersion": "0.8" 12 | } -------------------------------------------------------------------------------- /.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 | logs 24 | .factorypath 25 | /.apt_generated/ 26 | /.apt_generated_tests/ 27 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/models/block/click_machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube", 3 | "textures": { 4 | "north": "clickmachine:blocks/side", 5 | "east": "clickmachine:blocks/side", 6 | "south": "clickmachine:blocks/front", 7 | "west": "clickmachine:blocks/side", 8 | "up": "clickmachine:blocks/side", 9 | "down": "clickmachine:blocks/side", 10 | "particle": "clickmachine:blocks/side" 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/models/item/click_machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube", 3 | "textures": { 4 | "north": "clickmachine:blocks/front", 5 | "east": "clickmachine:blocks/side", 6 | "south": "clickmachine:blocks/side", 7 | "west": "clickmachine:blocks/side", 8 | "up": "clickmachine:blocks/side", 9 | "down": "clickmachine:blocks/side", 10 | "particle": "clickmachine:blocks/side" 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/models/block/click_machine_off.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "block/cube", 3 | "textures": { 4 | "north": "clickmachine:blocks/side", 5 | "east": "clickmachine:blocks/side", 6 | "south": "clickmachine:blocks/front_off", 7 | "west": "clickmachine:blocks/side", 8 | "up": "clickmachine:blocks/side", 9 | "down": "clickmachine:blocks/side", 10 | "particle": "clickmachine:blocks/side" 11 | } 12 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/es_ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Maquina De Clics", 3 | "block.clickmachine.auto_clicker": "Maquina De Clics", 4 | "gui.clickmachine.speed": "%s clicks/s", 5 | "gui.clickmachine.sneaking": "Shiftear", 6 | "gui.clickmachine.right_click": "Click Derecho", 7 | "gui.clickmachine.power": "FE: %s/%s", 8 | "gui.clickmachine.power.usage": "Usa %s FE/t", 9 | "gui.clickmachine.rainbow_magic": "Impulsado Por Magia Arcoiris!" 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/es_es.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Maquina De Clics", 3 | "block.clickmachine.auto_clicker": "Maquina De Clics", 4 | "gui.clickmachine.speed": "%s clicks/s", 5 | "gui.clickmachine.sneaking": "Shiftear", 6 | "gui.clickmachine.right_click": "Click Derecho", 7 | "gui.clickmachine.power": "FE: %s/%s", 8 | "gui.clickmachine.power.usage": "Usa %s FE/t", 9 | "gui.clickmachine.rainbow_magic": "Impulsado Por Magia Arcoiris!" 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/es_mx.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Maquina De Clics", 3 | "block.clickmachine.auto_clicker": "Maquina De Clics", 4 | "gui.clickmachine.speed": "%s clicks/s", 5 | "gui.clickmachine.sneaking": "Shiftear", 6 | "gui.clickmachine.right_click": "Click Derecho", 7 | "gui.clickmachine.power": "FE: %s/%s", 8 | "gui.clickmachine.power.usage": "Usa %s FE/t", 9 | "gui.clickmachine.rainbow_magic": "Impulsado Por Magia Arcoiris!" 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Авто-Использователь", 3 | "block.clickmachine.click_machine": "Авто-Использователь", 4 | "gui.clickmachine.speed": "%s клик/с", 5 | "gui.clickmachine.sneaking": "Режим SHIFT+ЛКМ", 6 | "gui.clickmachine.right_click": "Режим ПКМ", 7 | "gui.clickmachine.power": "FE: %s/%s", 8 | "gui.clickmachine.power.usage": "Использует %s FE/t", 9 | "gui.clickmachine.rainbow_magic": "Работает на Радужной Магии!" 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/it_it.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Macchina Auto-Cliccante", 3 | "block.clickmachine.click_machine": "Macchina Auto-Cliccante", 4 | "gui.clickmachine.speed": "%s click/s", 5 | "gui.clickmachine.sneaking": "Abbassato", 6 | "gui.clickmachine.right_click": "Click tasto destro", 7 | "gui.clickmachine.power": "FE: %s/%s", 8 | "gui.clickmachine.power.usage": "Uso di %s FE/t", 9 | "gui.clickmachine.rainbow_magic": "Alimentato ad Energia Magica!" 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/tr_tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Tıklama Makinesi", 3 | 4 | "block.clickmachine.auto_clicker": "Tıklama Makinesi", 5 | 6 | "gui.clickmachine.speed": "%s tıklama/s", 7 | "gui.clickmachine.sneaking": "Eğiliniyor", 8 | "gui.clickmachine.right_click": "Sağ Tıklanıyor", 9 | "gui.clickmachine.power": "FE: %s/%s", 10 | "gui.clickmachine.power.usage": "%s FE/t Kullanılıyor", 11 | "gui.clickmachine.rainbow_magic": "Rainbow Magic Tarafından Desteklenmektedir!" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Click Machine", 3 | "block.clickmachine.click_machine": "Click Machine", 4 | "gui.clickmachine.speed": "%s clicks/s", 5 | "gui.clickmachine.speed_slow": "1 click/%ss", 6 | "gui.clickmachine.sneaking": "Sneaking", 7 | "gui.clickmachine.right_click": "Right-Clicking", 8 | "gui.clickmachine.power": "FE: %s/%s", 9 | "gui.clickmachine.power.usage": "Using %s FE/t", 10 | "gui.clickmachine.rainbow_magic": "Powered by Rainbow Magic!" 11 | } -------------------------------------------------------------------------------- /src/generated/resources/data/clickmachine/loot_table/blocks/click_machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "bonus_rolls": 0.0, 6 | "conditions": [ 7 | { 8 | "condition": "minecraft:survives_explosion" 9 | } 10 | ], 11 | "entries": [ 12 | { 13 | "type": "minecraft:item", 14 | "name": "clickmachine:click_machine" 15 | } 16 | ], 17 | "rolls": 1.0 18 | } 19 | ], 20 | "random_sequence": "clickmachine:blocks/click_machine" 21 | } -------------------------------------------------------------------------------- /src/generated/resources/data/clickmachine/recipe/click_machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "category": "misc", 4 | "group": "clickmachine", 5 | "key": { 6 | "C": { 7 | "item": "minecraft:chorus_flower" 8 | }, 9 | "D": { 10 | "item": "minecraft:diorite" 11 | }, 12 | "S": { 13 | "tag": "c:storage_blocks/redstone" 14 | } 15 | }, 16 | "pattern": [ 17 | "DDD", 18 | "DCD", 19 | "DSD" 20 | ], 21 | "result": { 22 | "count": 1, 23 | "id": "clickmachine:click_machine" 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/ClickMachineClient.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine; 2 | 3 | import dev.shadowsoffire.clickmachine.gui.ClickMachineScreen; 4 | import net.neoforged.api.distmarker.Dist; 5 | import net.neoforged.bus.api.SubscribeEvent; 6 | import net.neoforged.fml.common.EventBusSubscriber; 7 | import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; 8 | 9 | @EventBusSubscriber(modid = ClickMachine.MODID, value = Dist.CLIENT) 10 | public class ClickMachineClient { 11 | 12 | @SubscribeEvent 13 | public static void setup(RegisterMenuScreensEvent e) { 14 | e.register(ClickMachine.CLICK_MACHINE_MENU, ClickMachineScreen::new); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/templates/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | 3 | loaderVersion="[2,)" 4 | 5 | license="${license}" 6 | 7 | [[mods]] #mandatory 8 | 9 | modId="${modid}" 10 | 11 | version="${version}" 12 | 13 | displayName="${modName}" 14 | 15 | authors="${author}" 16 | 17 | description='''${desc}''' 18 | 19 | ${mixinConfig} 20 | 21 | [[dependencies.${modid}]] 22 | modId="minecraft" 23 | type="required" 24 | versionRange="[${mcVersion},)" 25 | ordering="NONE" 26 | side="BOTH" 27 | 28 | [[dependencies.${modid}]] 29 | modId="neoforge" 30 | type="required" 31 | versionRange="[${forgeVersion},)" 32 | ordering="NONE" 33 | side="BOTH" 34 | 35 | [[dependencies.${modid}]] 36 | modId="placebo" 37 | type="required" 38 | versionRange="[${placeboVersion},)" 39 | ordering="NONE" 40 | side="BOTH" 41 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 2 | 3 | # Basic Information 4 | fileName=ClickMachine 5 | modName=Click Machine 6 | modGroup=dev.shadowsoffire 7 | modid=clickmachine 8 | author=Shadows_of_Fire 9 | desc=A standalone auto-clicker mod, similar to the old Autonomous Actuator. 10 | version=9.0.1 11 | 12 | # Dependencies 13 | mcVersion=1.21.1 14 | javaVersion=21 15 | forgeVersion=21.1.187 16 | parchmentVersion=2024.11.17-1.21.1 17 | jeiVersion=19.21.2.313 18 | jadeVersion=15.10.2 19 | #curiosVersion=9.5.1+1.21.1 20 | placeboVersion=9.9.0 21 | #patchouliVersion=1.19.2-76 22 | 23 | # Mixins 24 | mixin=true 25 | mixinVersion=0.8.7 26 | 27 | # Publishing 28 | requiredDeps=placebo 29 | #optionalDeps= 30 | #incompatibleDeps= 31 | releaseType=release 32 | modLoaders=NeoForge 33 | curseforgeProjectId=298275 34 | 35 | # Tests 36 | enableTests=false 37 | -------------------------------------------------------------------------------- /src/generated/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | 3 | loaderVersion="[2,)" 4 | 5 | license="MIT License" 6 | 7 | [[mods]] #mandatory 8 | 9 | modId="clickmachine" 10 | 11 | version="9.0.1" 12 | 13 | displayName="Click Machine" 14 | 15 | authors="Shadows_of_Fire" 16 | 17 | description='''A standalone auto-clicker mod, similar to the old Autonomous Actuator.''' 18 | 19 | [[mixins]] 20 | config="clickmachine.mixins.json" 21 | 22 | [[dependencies.clickmachine]] 23 | modId="minecraft" 24 | type="required" 25 | versionRange="[1.21.1,)" 26 | ordering="NONE" 27 | side="BOTH" 28 | 29 | [[dependencies.clickmachine]] 30 | modId="neoforge" 31 | type="required" 32 | versionRange="[21.1.187,)" 33 | ordering="NONE" 34 | side="BOTH" 35 | 36 | [[dependencies.clickmachine]] 37 | modId="placebo" 38 | type="required" 39 | versionRange="[9.9.0,)" 40 | ordering="NONE" 41 | side="BOTH" 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Brennan Ward 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 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/data/ClickRecipes.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.data; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | import dev.shadowsoffire.clickmachine.ClickMachine; 6 | import dev.shadowsoffire.placebo.datagen.LegacyRecipeProvider; 7 | import net.minecraft.core.HolderLookup; 8 | import net.minecraft.data.PackOutput; 9 | import net.minecraft.data.recipes.RecipeOutput; 10 | import net.minecraft.world.item.Items; 11 | import net.neoforged.neoforge.common.Tags; 12 | 13 | public class ClickRecipes extends LegacyRecipeProvider { 14 | 15 | public ClickRecipes(PackOutput output, CompletableFuture registries) { 16 | super(output, registries, ClickMachine.MODID); 17 | } 18 | 19 | @Override 20 | protected void genRecipes(RecipeOutput recipeOutput, HolderLookup.Provider registries) { 21 | addShaped(ClickMachine.CLICK_MACHINE, 3, 3, 22 | Items.DIORITE, Items.DIORITE, Items.DIORITE, 23 | Items.DIORITE, Items.CHORUS_FLOWER, Items.DIORITE, 24 | Items.DIORITE, Tags.Items.STORAGE_BLOCKS_REDSTONE, Items.DIORITE); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/blockstates/click_machine.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "facing=up,active=true": { 4 | "x": 90, 5 | "model": "clickmachine:block/click_machine" 6 | }, 7 | "facing=down,active=true": { 8 | "x": 270, 9 | "model": "clickmachine:block/click_machine" 10 | }, 11 | "facing=east,active=true": { 12 | "y": 270, 13 | "model": "clickmachine:block/click_machine" 14 | }, 15 | "facing=south,active=true": { 16 | "model": "clickmachine:block/click_machine" 17 | }, 18 | "facing=west,active=true": { 19 | "y": 90, 20 | "model": "clickmachine:block/click_machine" 21 | }, 22 | "facing=north,active=true": { 23 | "y": 180, 24 | "model": "clickmachine:block/click_machine" 25 | }, 26 | "facing=up,active=false": { 27 | "x": 90, 28 | "model": "clickmachine:block/click_machine_off" 29 | }, 30 | "facing=down,active=false": { 31 | "x": 270, 32 | "model": "clickmachine:block/click_machine_off" 33 | }, 34 | "facing=east,active=false": { 35 | "y": 270, 36 | "model": "clickmachine:block/click_machine_off" 37 | }, 38 | "facing=south,active=false": { 39 | "model": "clickmachine:block/click_machine_off" 40 | }, 41 | "facing=west,active=false": { 42 | "y": 90, 43 | "model": "clickmachine:block/click_machine_off" 44 | }, 45 | "facing=north,active=false": { 46 | "y": 180, 47 | "model": "clickmachine:block/click_machine_off" 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "自动点击器", 3 | "block.clickmachine.click_machine": "自动点击器", 4 | "gui.clickmachine.speed_0.rf.tooltip": "速度0:每%sticks点击一次,%srf/t", 5 | "gui.clickmachine.speed_1.rf.tooltip": "速度1:每%sticks点击一次,%srf/t", 6 | "gui.clickmachine.speed_2.rf.tooltip": "速度2:每%sticks点击一次,%srf/t", 7 | "gui.clickmachine.speed_3.rf.tooltip": "速度3:每%sticks点击一次,%srf/t", 8 | "gui.clickmachine.speed_4.rf.tooltip": "速度4:每%sticks点击一次,%srf/t", 9 | "gui.clickmachine.speed_5.rf.tooltip": "速度5:每%sticks点击一次,%srf/t", 10 | "gui.clickmachine.speed_6.rf.tooltip": "速度6:每%sticks点击一次,%srf/t", 11 | "gui.clickmachine.speed_7.rf.tooltip": "速度7:每%sticks点击一次,%srf/t", 12 | "gui.clickmachine.speed_8.rf.tooltip": "速度8:每%sticks点击一次,%srf/t", 13 | "gui.clickmachine.speed_0.tooltip": "速度0:每%sticks点击一次", 14 | "gui.clickmachine.speed_1.tooltip": "速度1:每%sticks点击一次", 15 | "gui.clickmachine.speed_2.tooltip": "速度2:每%sticks点击一次", 16 | "gui.clickmachine.speed_3.tooltip": "速度3:每%sticks点击一次", 17 | "gui.clickmachine.speed_4.tooltip": "速度4:每%sticks点击一次", 18 | "gui.clickmachine.speed_5.tooltip": "速度5:每%sticks点击一次", 19 | "gui.clickmachine.speed_6.tooltip": "速度6:每%sticks点击一次", 20 | "gui.clickmachine.speed_7.tooltip": "速度7:每%sticks点击一次", 21 | "gui.clickmachine.speed_8.tooltip": "速度8:每%sticks点击一次", 22 | "gui.clickmachine.sneak.tooltip": "切换为潜行", 23 | "gui.clickmachine.left_click.tooltip": "左键点击", 24 | "gui.clickmachine.right_click.tooltip": "右键点击", 25 | "gui.clickmachine.power.tooltip": "能量:%s/%s" 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/de_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Auto-Klicker", 3 | "block.clickmachine.click_machine": "Auto-Klicker", 4 | "gui.clickmachine.speed_0.rf.tooltip": "Tempo 0: Ein Klick alle %s Ticks, %s rf/t", 5 | "gui.clickmachine.speed_1.rf.tooltip": "Tempo 1: Ein Klick alle %s Ticks, %s rf/t", 6 | "gui.clickmachine.speed_2.rf.tooltip": "Tempo 2: Ein Klick alle %s Ticks, %s rf/t", 7 | "gui.clickmachine.speed_3.rf.tooltip": "Tempo 3: Ein Klick alle %s Ticks, %s rf/t", 8 | "gui.clickmachine.speed_4.rf.tooltip": "Tempo 4: Ein Klick alle %s Ticks, %s rf/t", 9 | "gui.clickmachine.speed_5.rf.tooltip": "Tempo 5: Ein Klick alle %s Ticks, %s rf/t", 10 | "gui.clickmachine.speed_6.rf.tooltip": "Tempo 6: Ein Klick alle %s Ticks, %s rf/t", 11 | "gui.clickmachine.speed_7.rf.tooltip": "Tempo 7: Ein Klick alle %s Ticks, %s rf/t", 12 | "gui.clickmachine.speed_8.rf.tooltip": "Tempo 8: Ein Klick alle %s Ticks, %s rf/t", 13 | "gui.clickmachine.speed_0.tooltip": "Tempo 0: Ein Klick alle %s Ticks", 14 | "gui.clickmachine.speed_1.tooltip": "Tempo 1: Ein Klick alle %s Ticks", 15 | "gui.clickmachine.speed_2.tooltip": "Tempo 2: Ein Klick alle %s Ticks", 16 | "gui.clickmachine.speed_3.tooltip": "Tempo 3: Ein Klick alle %s Ticks", 17 | "gui.clickmachine.speed_4.tooltip": "Tempo 4: Ein Klick alle %s Ticks", 18 | "gui.clickmachine.speed_5.tooltip": "Tempo 5: Ein Klick alle %s Ticks", 19 | "gui.clickmachine.speed_6.tooltip": "Tempo 6: Ein Klick alle %s Ticks", 20 | "gui.clickmachine.speed_7.tooltip": "Tempo 7: Ein Klick alle %s Ticks", 21 | "gui.clickmachine.speed_8.tooltip": "Tempo 8: Ein Klick alle %s Ticks", 22 | "gui.clickmachine.sneak.tooltip": "Schleichen umschalten", 23 | "gui.clickmachine.left_click.tooltip": "Linksklick", 24 | "gui.clickmachine.right_click.tooltip": "Rechtsklick", 25 | "gui.clickmachine.power.tooltip": "Strom: %s/%s" 26 | } -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/gui/ClickMachineMenu.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.gui; 2 | 3 | import dev.shadowsoffire.clickmachine.ClickMachine; 4 | import dev.shadowsoffire.clickmachine.block.ClickMachineTile; 5 | import dev.shadowsoffire.placebo.menu.BlockEntityMenu; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.entity.player.Inventory; 8 | import net.minecraft.world.entity.player.Player; 9 | import net.neoforged.neoforge.items.SlotItemHandler; 10 | 11 | public class ClickMachineMenu extends BlockEntityMenu { 12 | 13 | public ClickMachineMenu(int id, Inventory pInv, BlockPos pos) { 14 | super(ClickMachine.CLICK_MACHINE_MENU, id, pInv, pos); 15 | this.addSlot(new SlotItemHandler(this.tile.getHandler(), 0, 8, 50)); // Add clicker item slot 16 | this.addPlayerSlots(pInv, 8, 114); 17 | this.mover.registerRule((stack, slot) -> slot == 0, 1, this.slots.size()); 18 | this.mover.registerRule((stack, slot) -> slot > 0, 0, 1); 19 | } 20 | 21 | @Override 22 | public boolean stillValid(Player player) { 23 | return this.tile != null && !this.tile.isRemoved(); 24 | } 25 | 26 | @Override 27 | public boolean clickMenuButton(Player pPlayer, int button) { 28 | if (button == 2) this.tile.setSneaking(!this.tile.isSneaking()); 29 | else if (button == 3) this.tile.setRightClicking(!this.tile.isRightClicking()); 30 | else if (button >= 4 && button <= 12) { 31 | this.tile.setSpeedIndex(button - 4); 32 | } 33 | return button <= 12; 34 | } 35 | 36 | public boolean isSneaking() { 37 | return this.tile.isSneaking(); 38 | } 39 | 40 | public boolean isRightClicking() { 41 | return this.tile.isRightClicking(); 42 | } 43 | 44 | public int getEnergy() { 45 | return this.tile.getPower(); 46 | } 47 | 48 | public int getSpeedIdx() { 49 | return this.tile.getSpeedIndex(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/assets/clickmachine/lang/pt_br.json: -------------------------------------------------------------------------------- 1 | { 2 | "gui.clickmachine.autoclick": "Auto Clicker", 3 | "block.clickmachine.click_machine": "Auto Clicker", 4 | "gui.clickmachine.speed_0.rf.tooltip": "Velocidade 0: Um clique a cada %s ticks, %s rf/t", 5 | "gui.clickmachine.speed_1.rf.tooltip": "Velocidade 1: Um clique a cada %s ticks, %s rf/t", 6 | "gui.clickmachine.speed_2.rf.tooltip": "Velocidade 2: Um clique a cada %s ticks, %s rf/t", 7 | "gui.clickmachine.speed_3.rf.tooltip": "Velocidade 3: Um clique a cada %s ticks, %s rf/t", 8 | "gui.clickmachine.speed_4.rf.tooltip": "Velocidade 4: Um clique a cada %s ticks, %s rf/t", 9 | "gui.clickmachine.speed_5.rf.tooltip": "Velocidade 5: Um clique a cada %s ticks, %s rf/t", 10 | "gui.clickmachine.speed_6.rf.tooltip": "Velocidade 6: Um clique a cada %s ticks, %s rf/t", 11 | "gui.clickmachine.speed_7.rf.tooltip": "Velocidade 7: Um clique a cada %s ticks, %s rf/t", 12 | "gui.clickmachine.speed_8.rf.tooltip": "Velocidade 8: Um clique a cada %s ticks, %s rf/t", 13 | "gui.clickmachine.speed_0.tooltip": "Velocidade 0: Um clique a cada %s ticks", 14 | "gui.clickmachine.speed_1.tooltip": "Velocidade 1: Um clique a cada %s ticks", 15 | "gui.clickmachine.speed_2.tooltip": "Velocidade 2: Um clique a cada %s ticks", 16 | "gui.clickmachine.speed_3.tooltip": "Velocidade 3: Um clique a cada %s ticks", 17 | "gui.clickmachine.speed_4.tooltip": "Velocidade 4: Um clique a cada %s ticks", 18 | "gui.clickmachine.speed_5.tooltip": "Velocidade 5: Um clique a cada %s ticks", 19 | "gui.clickmachine.speed_6.tooltip": "Velocidade 6: Um clique a cada %s ticks", 20 | "gui.clickmachine.speed_7.tooltip": "Velocidade 7: Um clique a cada %s ticks", 21 | "gui.clickmachine.speed_8.tooltip": "Velocidade 8: Um clique a cada %s ticks", 22 | "gui.clickmachine.sneak.tooltip": "Toggle Sneaking", 23 | "gui.clickmachine.left_click.tooltip": "Clique esquerdo", 24 | "gui.clickmachine.right_click.tooltip": "Clique direito", 25 | "gui.clickmachine.power.tooltip": "Potência: %s/%s" 26 | } -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/data/LootProvider.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.data; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import java.util.concurrent.CompletableFuture; 6 | 7 | import dev.shadowsoffire.clickmachine.ClickMachine; 8 | import net.minecraft.core.Holder; 9 | import net.minecraft.core.HolderLookup.Provider; 10 | import net.minecraft.core.registries.BuiltInRegistries; 11 | import net.minecraft.data.PackOutput; 12 | import net.minecraft.data.loot.BlockLootSubProvider; 13 | import net.minecraft.data.loot.LootTableProvider; 14 | import net.minecraft.resources.ResourceKey; 15 | import net.minecraft.world.flag.FeatureFlags; 16 | import net.minecraft.world.item.Item; 17 | import net.minecraft.world.level.block.Block; 18 | import net.minecraft.world.level.storage.loot.LootTable; 19 | import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; 20 | 21 | public class LootProvider extends LootTableProvider { 22 | 23 | private LootProvider(PackOutput output, Set> requiredTables, List subProviders, CompletableFuture registries) { 24 | super(output, requiredTables, subProviders, registries); 25 | } 26 | 27 | public static LootProvider create(PackOutput output, CompletableFuture registries) { 28 | return new LootProvider( 29 | output, 30 | Set.of(), 31 | List.of( 32 | new LootTableProvider.SubProviderEntry(BlockLoot::new, LootContextParamSets.BLOCK)), 33 | registries); 34 | } 35 | 36 | public static class BlockLoot extends BlockLootSubProvider { 37 | 38 | public static final Set EXPLOSION_RESISTANT = Set.of(); 39 | 40 | protected BlockLoot(Provider registries) { 41 | super(EXPLOSION_RESISTANT, FeatureFlags.REGISTRY.allFlags(), registries); 42 | } 43 | 44 | @Override 45 | protected void generate() { 46 | this.dropSelf(ClickMachine.CLICK_MACHINE); 47 | } 48 | 49 | @Override 50 | protected Iterable getKnownBlocks() { 51 | return BuiltInRegistries.BLOCK.holders().filter(h -> h.getKey().location().getNamespace().equals(ClickMachine.MODID)).map(Holder::value).toList(); 52 | } 53 | 54 | protected void dropSelf(Holder block) { 55 | this.dropSelf(block.value()); 56 | } 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## Future 2 | * Added support for holding down left or right click. 3 | 4 | ## 9.0.1 5 | * Updated to Placebo 9.9.0 6 | * SrNadien: Added Spanish translation. 7 | * RuyaSavascisi: Added Turkish translation. 8 | 9 | ## 9.0.0 10 | * Updated to 1.21.1. 11 | * Made the config synced from client to server. 12 | * Changed display logic to show seconds-per-click when the speed is less than 1 click/s. 13 | 14 | ## 8.0.2 15 | * Fixed the config loading too early to blacklist non-vanilla items. 16 | 17 | ## 8.0.1 18 | * Removed forge dependency line from the mods.toml and marked as Forge and NeoForge for CF. 19 | * The dependency will be added back and the Forge marker will be removed once CF supports Neo correctly. 20 | 21 | ## 8.0.0 22 | * Updated to 1.20.1 23 | 24 | ## 7.0.1 25 | * Added support for item cooldowns. When an item with a cooldown is used, the cooldown period must elapse before using it again. 26 | * The previous behavior was that an item with a cooldown could never be used again until the block was broken. 27 | * This change also means that each clicker is now allocated its own fake player, instead of a shared one per uuid. 28 | 29 | ## 7.0.0 30 | * Updated to 1.19.2 31 | 32 | ## 6.0.5 33 | * Follow-up fix to a bug added in 6.0.4, the ray-trace now properly uses the eye position instead of the feet position. 34 | * When the click machine is using items with extra reach distance, the range of the machine will increase accordingly. 35 | 36 | ## 6.0.4 37 | * Fixed startup crash from registry issues. 38 | * Fixed Click Machine being unable to use buckets. 39 | * This is done by changing the position of the fake player to be 2 blocks lower, so the eye pos is where the "click" happens from. 40 | 41 | ## 6.0.3 42 | * Rebuilt for 1.18.2 43 | 44 | ## 6.0.2 45 | * Updated to Placebo 6.1.0 46 | * Fixed a dupe bug. 47 | 48 | ## 6.0.1 49 | * Fixed TE data not saving. 50 | * Hav3funDJ: Added Italian Translation. 51 | 52 | ## 6.0.0 53 | * Updated to 1.18.1 54 | 55 | ## 5.0.0 56 | * Updated to 1.17.1 57 | 58 | ## 4.5.0 59 | * Added a dummy rainbow power bar when power is disabled, to hopefully make people realize power is disabled. 60 | * Removed the Energy Capability when power is disabled, so nothing tries to display it. 61 | 62 | ## 4.4.1 63 | * Made it so the Click Machine fake player cannot be teleported, which crashes normally. 64 | 65 | ## 4.4.0 66 | * Click Machine has received a brand new GUI! - https://i.imgur.com/7OeVssp.png 67 | * The texture of the Click Machine will stop its animation when shut off by redstone. -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/gui/SpeedSlider.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.gui; 2 | 3 | import dev.shadowsoffire.clickmachine.ClickMachineConfig; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.client.gui.components.AbstractSliderButton; 6 | import net.minecraft.network.chat.CommonComponents; 7 | import net.minecraft.network.chat.Component; 8 | import net.minecraft.util.Mth; 9 | 10 | public class SpeedSlider extends AbstractSliderButton { 11 | 12 | protected static final int minValue = 0, maxValue = 8; 13 | protected static final float stepSize = 1 / 9F; 14 | 15 | protected final ClickMachineScreen gui; 16 | 17 | public SpeedSlider(ClickMachineScreen gui, int x, int y, int width, int height) { 18 | super(x, y, width, height, CommonComponents.EMPTY, normalizeValue(gui.getMenu().getSpeedIdx())); 19 | this.gui = gui; 20 | this.updateMessage(); 21 | } 22 | 23 | @Override 24 | protected void updateMessage() { 25 | int spd = denormalizeValue(this.value); 26 | int ticksPerClick = ClickMachineConfig.speeds[spd]; 27 | if (ticksPerClick <= 20) { 28 | double cps = 1D / ticksPerClick * 20; 29 | this.setMessage(Component.translatable("gui.clickmachine.speed", String.format("%.2f", cps))); 30 | } 31 | else { 32 | double spc = ticksPerClick / 20D; 33 | this.setMessage(Component.translatable("gui.clickmachine.speed_slow", String.format("%.2f", spc))); 34 | } 35 | } 36 | 37 | @Override 38 | protected void applyValue() { 39 | Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.gui.getMenu().containerId, 4 + denormalizeValue(this.value)); 40 | } 41 | 42 | public void setValue(int value) { 43 | if (!this.gui.isDragging()) { 44 | this.value = normalizeValue(value); 45 | this.updateMessage(); 46 | } 47 | } 48 | 49 | /** 50 | * Converts an int value within the range into a slider percentage. 51 | */ 52 | public static double normalizeValue(double value) { 53 | return Mth.clamp((snapToStepClamp(value) - minValue) / (maxValue - minValue), 0.0D, 1.0D); 54 | } 55 | 56 | /** 57 | * Converts a slider percentage to its bounded int value. 58 | */ 59 | public static int denormalizeValue(double value) { 60 | return (int) snapToStepClamp(Mth.lerp(Mth.clamp(value, 0.0D, 1.0D), minValue, maxValue)); 61 | } 62 | 63 | private static double snapToStepClamp(double valueIn) { 64 | if (stepSize > 0.0F) { 65 | valueIn = stepSize * Math.round(valueIn / stepSize); 66 | } 67 | 68 | return Mth.clamp(valueIn, minValue, maxValue); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /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/main/java/dev/shadowsoffire/clickmachine/gui/ClickerCheckboxButton.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.gui; 2 | 3 | import java.util.function.BooleanSupplier; 4 | 5 | import com.mojang.blaze3d.systems.RenderSystem; 6 | 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.Font; 9 | import net.minecraft.client.gui.GuiGraphics; 10 | import net.minecraft.client.gui.components.AbstractButton; 11 | import net.minecraft.client.gui.narration.NarrationElementOutput; 12 | import net.minecraft.network.chat.Component; 13 | import net.minecraft.resources.ResourceLocation; 14 | 15 | public class ClickerCheckboxButton extends AbstractButton { 16 | 17 | private static final ResourceLocation CHECKBOX_SELECTED_HIGHLIGHTED_SPRITE = ResourceLocation.withDefaultNamespace("widget/checkbox_selected_highlighted"); 18 | private static final ResourceLocation CHECKBOX_SELECTED_SPRITE = ResourceLocation.withDefaultNamespace("widget/checkbox_selected"); 19 | private static final ResourceLocation CHECKBOX_HIGHLIGHTED_SPRITE = ResourceLocation.withDefaultNamespace("widget/checkbox_highlighted"); 20 | private static final ResourceLocation CHECKBOX_SPRITE = ResourceLocation.withDefaultNamespace("widget/checkbox"); 21 | 22 | protected final ClickMachineScreen gui; 23 | protected final int index; 24 | protected final BooleanSupplier selected; 25 | 26 | public ClickerCheckboxButton(ClickMachineScreen gui, int x, int y, int width, int height, Component title, int index, BooleanSupplier selected) { 27 | super(x, y, width, height, title); 28 | this.gui = gui; 29 | this.index = index; 30 | this.selected = selected; 31 | } 32 | 33 | @Override 34 | public void onPress() { 35 | Minecraft.getInstance().gameMode.handleInventoryButtonClick(this.gui.getMenu().containerId, this.index - 1); 36 | } 37 | 38 | @Override 39 | public boolean isFocused() { 40 | return super.isHovered() || super.isFocused(); 41 | } 42 | 43 | public boolean isSelected() { 44 | return selected.getAsBoolean(); 45 | } 46 | 47 | @Override 48 | public void renderWidget(GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) { 49 | Minecraft minecraft = Minecraft.getInstance(); 50 | RenderSystem.enableDepthTest(); 51 | Font font = minecraft.font; 52 | gfx.setColor(1.0F, 1.0F, 1.0F, this.alpha); 53 | RenderSystem.enableBlend(); 54 | RenderSystem.defaultBlendFunc(); 55 | ResourceLocation sprite; 56 | if (this.isSelected()) { 57 | sprite = this.isFocused() ? CHECKBOX_SELECTED_HIGHLIGHTED_SPRITE : CHECKBOX_SELECTED_SPRITE; 58 | } 59 | else { 60 | sprite = this.isFocused() ? CHECKBOX_HIGHLIGHTED_SPRITE : CHECKBOX_SPRITE; 61 | } 62 | gfx.blitSprite(sprite, this.getX(), this.getY(), this.width, this.height); 63 | gfx.drawString(font, this.getMessage().getString(), this.getX() + 24, this.getY() + (this.height - 8) / 2, 4210752, false); 64 | } 65 | 66 | @Override 67 | protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {} 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/block/ClickMachineBlock.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.block; 2 | 3 | import dev.shadowsoffire.clickmachine.gui.ClickMachineMenu; 4 | import dev.shadowsoffire.placebo.block_entity.TickingEntityBlock; 5 | import dev.shadowsoffire.placebo.menu.MenuUtil; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Direction; 8 | import net.minecraft.util.Mth; 9 | import net.minecraft.world.InteractionResult; 10 | import net.minecraft.world.entity.LivingEntity; 11 | import net.minecraft.world.entity.player.Player; 12 | import net.minecraft.world.item.ItemStack; 13 | import net.minecraft.world.item.context.BlockPlaceContext; 14 | import net.minecraft.world.level.Level; 15 | import net.minecraft.world.level.LevelAccessor; 16 | import net.minecraft.world.level.block.Block; 17 | import net.minecraft.world.level.block.Mirror; 18 | import net.minecraft.world.level.block.Rotation; 19 | import net.minecraft.world.level.block.SoundType; 20 | import net.minecraft.world.level.block.entity.BlockEntity; 21 | import net.minecraft.world.level.block.state.BlockBehaviour; 22 | import net.minecraft.world.level.block.state.BlockState; 23 | import net.minecraft.world.level.block.state.StateDefinition.Builder; 24 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 25 | import net.minecraft.world.level.block.state.properties.BooleanProperty; 26 | import net.minecraft.world.level.block.state.properties.DirectionProperty; 27 | import net.minecraft.world.level.material.MapColor; 28 | import net.minecraft.world.phys.BlockHitResult; 29 | 30 | public class ClickMachineBlock extends Block implements TickingEntityBlock { 31 | 32 | public static final DirectionProperty FACING = BlockStateProperties.FACING; 33 | public static final BooleanProperty ACTIVE = BooleanProperty.create("active"); 34 | 35 | public ClickMachineBlock() { 36 | super(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).strength(5)); 37 | this.registerDefaultState(this.defaultBlockState().setValue(ACTIVE, true)); 38 | } 39 | 40 | @Override 41 | public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { 42 | return new ClickMachineTile(pPos, pState); 43 | } 44 | 45 | @Override 46 | protected void createBlockStateDefinition(Builder builder) { 47 | builder.add(FACING, ACTIVE); 48 | } 49 | 50 | @Override 51 | public BlockState rotate(BlockState state, LevelAccessor world, BlockPos pos, Rotation direction) { 52 | return state.setValue(FACING, direction.rotate(state.getValue(FACING))); 53 | } 54 | 55 | @Override 56 | public BlockState mirror(BlockState state, Mirror mirror) { 57 | return state.setValue(FACING, mirror.mirror(state.getValue(FACING))); 58 | } 59 | 60 | @Override 61 | public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) { 62 | BlockEntity te = world.getBlockEntity(pos); 63 | if (!world.isClientSide && te instanceof ClickMachineTile && placer instanceof Player) { 64 | ((ClickMachineTile) te).setPlayer((Player) placer); 65 | } 66 | } 67 | 68 | @Override 69 | public BlockState getStateForPlacement(BlockPlaceContext context) { 70 | Player placer = context.getPlayer(); 71 | Direction face = placer.getDirection().getOpposite(); 72 | if (placer.getXRot() > 50) face = Direction.UP; 73 | else if (placer.getXRot() < -50) face = Direction.DOWN; 74 | return this.defaultBlockState().setValue(FACING, face); 75 | } 76 | 77 | @Override 78 | protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { 79 | return MenuUtil.openGui(player, pos, ClickMachineMenu::new); 80 | } 81 | 82 | @Override 83 | @Deprecated 84 | public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) { 85 | if (state.getBlock() == this && newState.getBlock() == this) return; 86 | BlockEntity te = world.getBlockEntity(pos); 87 | if (te instanceof ClickMachineTile) popResource(world, pos, ((ClickMachineTile) te).held.getStackInSlot(0)); 88 | super.onRemove(state, world, pos, newState, isMoving); 89 | } 90 | 91 | @Override 92 | public boolean hasAnalogOutputSignal(BlockState pState) { 93 | return true; 94 | } 95 | 96 | @Override 97 | @Deprecated 98 | public int getAnalogOutputSignal(BlockState pBlockState, Level pLevel, BlockPos pPos) { 99 | BlockEntity te = pLevel.getBlockEntity(pPos); 100 | if (te instanceof ClickMachineTile) { 101 | ItemStack i = ((ClickMachineTile) te).held.getStackInSlot(0); 102 | if (i.isEmpty()) return 0; 103 | if (i.getMaxStackSize() == 1 && i.isDamageableItem()) { 104 | return Mth.floor(15F * (i.getMaxDamage() - i.getDamageValue()) / i.getMaxDamage()); 105 | } 106 | else return Mth.floor(15F * i.getCount() / i.getMaxStackSize()); 107 | } 108 | return 0; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/ClickMachine.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | 6 | import dev.shadowsoffire.clickmachine.ClickMachineConfig.ConfigPayload; 7 | import dev.shadowsoffire.clickmachine.block.ClickMachineBlock; 8 | import dev.shadowsoffire.clickmachine.block.ClickMachineTile; 9 | import dev.shadowsoffire.clickmachine.data.ClickRecipes; 10 | import dev.shadowsoffire.clickmachine.data.LootProvider; 11 | import dev.shadowsoffire.clickmachine.gui.ClickMachineMenu; 12 | import dev.shadowsoffire.clickmachine.util.FakePlayerUtil.UsefulFakePlayer; 13 | import dev.shadowsoffire.placebo.block_entity.TickingBlockEntityType.TickSide; 14 | import dev.shadowsoffire.placebo.config.Configuration; 15 | import dev.shadowsoffire.placebo.datagen.DataGenBuilder; 16 | import dev.shadowsoffire.placebo.network.PayloadHelper; 17 | import dev.shadowsoffire.placebo.registry.DeferredHelper; 18 | import dev.shadowsoffire.placebo.tabs.TabFillingRegistry; 19 | import dev.shadowsoffire.placebo.util.RunnableReloader; 20 | import net.minecraft.core.Holder; 21 | import net.minecraft.resources.ResourceLocation; 22 | import net.minecraft.world.inventory.MenuType; 23 | import net.minecraft.world.item.CreativeModeTabs; 24 | import net.minecraft.world.item.Item; 25 | import net.minecraft.world.level.block.Block; 26 | import net.minecraft.world.level.block.entity.BlockEntityType; 27 | import net.neoforged.bus.api.EventPriority; 28 | import net.neoforged.bus.api.IEventBus; 29 | import net.neoforged.bus.api.SubscribeEvent; 30 | import net.neoforged.fml.common.Mod; 31 | import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; 32 | import net.neoforged.neoforge.capabilities.Capabilities; 33 | import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; 34 | import net.neoforged.neoforge.common.NeoForge; 35 | import net.neoforged.neoforge.data.event.GatherDataEvent; 36 | import net.neoforged.neoforge.event.AddReloadListenerEvent; 37 | import net.neoforged.neoforge.event.OnDatapackSyncEvent; 38 | import net.neoforged.neoforge.event.entity.EntityEvent; 39 | import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; 40 | import net.neoforged.neoforge.network.PacketDistributor; 41 | 42 | @Mod(ClickMachine.MODID) 43 | public class ClickMachine { 44 | 45 | public static final String MODID = "clickmachine"; 46 | 47 | public static final Logger LOG = LogManager.getLogger(MODID); 48 | 49 | private static final DeferredHelper R = DeferredHelper.create(MODID); 50 | 51 | public static final Holder CLICK_MACHINE = R.block("click_machine", ClickMachineBlock::new); 52 | public static final Holder CLICK_MACHINE_ITEM = R.blockItem("click_machine", CLICK_MACHINE); 53 | public static final MenuType CLICK_MACHINE_MENU = R.menuWithPos("click_machine", ClickMachineMenu::new); 54 | public static final BlockEntityType CLICK_MACHINE_TILE = R.tickingBlockEntity("click_machine", ClickMachineTile::new, TickSide.SERVER, CLICK_MACHINE); 55 | 56 | public ClickMachine(IEventBus bus) { 57 | bus.register(this); 58 | bus.register(R); 59 | NeoForge.EVENT_BUS.register(new GameBusEvents()); 60 | } 61 | 62 | @SubscribeEvent 63 | public void setup(FMLCommonSetupEvent e) { 64 | e.enqueueWork(() -> { 65 | TabFillingRegistry.registerSimple(CreativeModeTabs.REDSTONE_BLOCKS, CLICK_MACHINE_ITEM.value()); 66 | }); 67 | PayloadHelper.registerPayload(new ConfigPayload.Provider()); 68 | } 69 | 70 | @SubscribeEvent 71 | public void data(GatherDataEvent e) { 72 | DataGenBuilder.create(MODID) 73 | .provider(ClickRecipes::new) 74 | .provider(LootProvider::create) 75 | .build(e); 76 | } 77 | 78 | @SubscribeEvent 79 | public void caps(RegisterCapabilitiesEvent e) { 80 | e.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, CLICK_MACHINE_TILE, (be, ctx) -> be.getEnergy()); 81 | e.registerBlockEntity(Capabilities.ItemHandler.BLOCK, CLICK_MACHINE_TILE, (be, ctx) -> be.getHandler()); 82 | } 83 | 84 | public static ResourceLocation loc(String path) { 85 | return ResourceLocation.fromNamespaceAndPath(MODID, path); 86 | } 87 | 88 | private class GameBusEvents { 89 | 90 | @SubscribeEvent(priority = EventPriority.HIGHEST) 91 | public void blockJoin(EntityJoinLevelEvent e) { 92 | if (e.getEntity() instanceof UsefulFakePlayer) e.setCanceled(true); 93 | } 94 | 95 | @SubscribeEvent(priority = EventPriority.LOWEST) 96 | public void dims(EntityEvent.Size e) { 97 | if (e.getEntity() instanceof UsefulFakePlayer) { 98 | e.setNewSize(e.getNewSize().withEyeHeight(0)); 99 | } 100 | } 101 | 102 | @SubscribeEvent 103 | public void reloads(AddReloadListenerEvent e) { 104 | e.addListener(RunnableReloader.of(() -> ClickMachineConfig.init(new Configuration(MODID)))); 105 | } 106 | 107 | @SubscribeEvent 108 | public void sync(OnDatapackSyncEvent e) { 109 | if (e.getPlayer() != null) { 110 | PacketDistributor.sendToPlayer(e.getPlayer(), new ConfigPayload()); 111 | } 112 | else { 113 | PacketDistributor.sendToAllPlayers(new ConfigPayload()); 114 | } 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/ClickMachineConfig.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine; 2 | 3 | import java.util.HashSet; 4 | import java.util.List; 5 | import java.util.Optional; 6 | import java.util.Set; 7 | 8 | import com.google.common.collect.ImmutableSet; 9 | import com.google.common.primitives.Ints; 10 | 11 | import dev.shadowsoffire.placebo.config.Configuration; 12 | import dev.shadowsoffire.placebo.network.PayloadProvider; 13 | import net.minecraft.core.registries.BuiltInRegistries; 14 | import net.minecraft.core.registries.Registries; 15 | import net.minecraft.network.ConnectionProtocol; 16 | import net.minecraft.network.RegistryFriendlyByteBuf; 17 | import net.minecraft.network.codec.ByteBufCodecs; 18 | import net.minecraft.network.codec.StreamCodec; 19 | import net.minecraft.network.protocol.PacketFlow; 20 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 21 | import net.minecraft.resources.ResourceLocation; 22 | import net.minecraft.world.item.Item; 23 | import net.minecraft.world.item.Items; 24 | import net.neoforged.neoforge.network.handling.IPayloadContext; 25 | 26 | public class ClickMachineConfig { 27 | 28 | public static int[] speeds = new int[] { 500, 200, 100, 50, 20, 10, 5, 2, 1 }; 29 | public static boolean usesRF = false; 30 | public static int maxPowerStorage = 50000; 31 | public static int[] powerPerSpeed = new int[] { 0, 3, 5, 10, 25, 50, 100, 250, 500 }; 32 | public static Set blacklistedItems = new HashSet<>(); 33 | 34 | public static void init(Configuration cfg) { 35 | 36 | String[] def = new String[9]; 37 | for (int i = 0; i < 9; i++) { 38 | def[i] = ((Integer) speeds[i]).toString(); 39 | } 40 | 41 | String[] unparsed = cfg.getStringList("Speeds", Configuration.CATEGORY_GENERAL, def, "The possible speeds of the auto clicker, in ticks between clicks (ex: 100 = 1 click every 5s). Must have 9 values."); 42 | 43 | for (int i = 0; i < 9; i++) { 44 | try { 45 | speeds[i] = Integer.parseInt(unparsed[i]); 46 | } 47 | catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) { 48 | ClickMachine.LOG.error("Failed to parse Speeds value for index {}, reverting to default.", i); 49 | ex.printStackTrace(); 50 | speeds[i] = Integer.parseInt(def[i]); 51 | } 52 | } 53 | 54 | usesRF = cfg.getBoolean("Uses RF", Configuration.CATEGORY_GENERAL, usesRF, "If the auto clicker uses RF"); 55 | 56 | def = new String[9]; 57 | for (int i = 0; i < 9; i++) { 58 | def[i] = ((Integer) powerPerSpeed[i]).toString(); 59 | } 60 | 61 | unparsed = cfg.getStringList("RF Costs", Configuration.CATEGORY_GENERAL, def, "The RF cost per tick for each speed, from 0-8. Must have 9 values. Unused if \"Uses RF\" = false"); 62 | 63 | for (int i = 0; i < 9; i++) { 64 | try { 65 | powerPerSpeed[i] = Integer.parseInt(unparsed[i]); 66 | } 67 | catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) { 68 | ClickMachine.LOG.error("Failed to parse RF Costs value for index {}, reverting to default.", i); 69 | ex.printStackTrace(); 70 | powerPerSpeed[i] = Integer.parseInt(def[i]); 71 | } 72 | } 73 | 74 | maxPowerStorage = cfg.getInt("Max Power Storage", Configuration.CATEGORY_GENERAL, maxPowerStorage, 0, Integer.MAX_VALUE, "How much power the auto clicker can store. Also the max input rate. Unused if \"Uses RF\" = false"); 75 | 76 | ImmutableSet.Builder set = ImmutableSet.builder(); 77 | String[] blacklist = cfg.getStringList("Item Blacklist", Configuration.CATEGORY_GENERAL, new String[] { "minecraft:bedrock" }, "Items that may not be held by the clicker"); 78 | for (String s : blacklist) { 79 | try { 80 | Item i = BuiltInRegistries.ITEM.get(ResourceLocation.parse(s)); 81 | if (i == Items.AIR) { 82 | throw new NullPointerException("Unknown Item: " + s); 83 | } 84 | set.add(i); 85 | } 86 | catch (Exception ex) { 87 | ClickMachine.LOG.error("Failed to parse unknown item \"{}\" in Click Machine Blacklist.", s); 88 | ex.printStackTrace(); 89 | } 90 | } 91 | blacklistedItems = set.build(); 92 | 93 | if (cfg.hasChanged()) cfg.save(); 94 | } 95 | 96 | public static record ConfigPayload(List speeds, boolean usesRF, int maxPower, List powerPerSpeed, Set blacklist) implements CustomPacketPayload { 97 | 98 | public static final Type TYPE = new Type<>(ClickMachine.loc("config")); 99 | 100 | public static final StreamCodec CODEC = StreamCodec.composite( 101 | ByteBufCodecs.VAR_INT.apply(ByteBufCodecs.list()), ConfigPayload::speeds, 102 | ByteBufCodecs.BOOL, ConfigPayload::usesRF, 103 | ByteBufCodecs.INT, ConfigPayload::maxPower, 104 | ByteBufCodecs.VAR_INT.apply(ByteBufCodecs.list()), ConfigPayload::powerPerSpeed, 105 | ByteBufCodecs.collection(HashSet::new, ByteBufCodecs.registry(Registries.ITEM)), ConfigPayload::blacklist, 106 | ConfigPayload::new); 107 | 108 | public ConfigPayload() { 109 | this(Ints.asList(ClickMachineConfig.speeds), ClickMachineConfig.usesRF, ClickMachineConfig.maxPowerStorage, Ints.asList(ClickMachineConfig.powerPerSpeed), ClickMachineConfig.blacklistedItems); 110 | } 111 | 112 | @Override 113 | public Type type() { 114 | return TYPE; 115 | } 116 | 117 | public static class Provider implements PayloadProvider { 118 | 119 | @Override 120 | public Type getType() { 121 | return TYPE; 122 | } 123 | 124 | @Override 125 | public StreamCodec getCodec() { 126 | return CODEC; 127 | } 128 | 129 | @Override 130 | public void handle(ConfigPayload msg, IPayloadContext ctx) { 131 | ClickMachineConfig.speeds = Ints.toArray(msg.speeds); 132 | ClickMachineConfig.usesRF = msg.usesRF; 133 | ClickMachineConfig.maxPowerStorage = msg.maxPower; 134 | ClickMachineConfig.powerPerSpeed = Ints.toArray(msg.powerPerSpeed); 135 | ClickMachineConfig.blacklistedItems = msg.blacklist; 136 | } 137 | 138 | @Override 139 | public List getSupportedProtocols() { 140 | return List.of(ConnectionProtocol.PLAY); 141 | } 142 | 143 | @Override 144 | public Optional getFlow() { 145 | return Optional.of(PacketFlow.CLIENTBOUND); 146 | } 147 | 148 | @Override 149 | public String getVersion() { 150 | return "1"; 151 | } 152 | 153 | } 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/gui/ClickMachineScreen.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.gui; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dev.shadowsoffire.clickmachine.ClickMachine; 7 | import dev.shadowsoffire.clickmachine.ClickMachineConfig; 8 | import dev.shadowsoffire.placebo.menu.IDataUpdateListener; 9 | import dev.shadowsoffire.placebo.screen.PlaceboContainerScreen; 10 | import dev.shadowsoffire.placebo.util.ClientUtil; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.gui.GuiGraphics; 13 | import net.minecraft.network.chat.Component; 14 | import net.minecraft.resources.ResourceLocation; 15 | import net.minecraft.world.entity.player.Inventory; 16 | import net.minecraft.world.entity.player.Player; 17 | 18 | public class ClickMachineScreen extends PlaceboContainerScreen implements IDataUpdateListener { 19 | 20 | public static final ResourceLocation GUI_TEXTURE = ClickMachine.loc("textures/gui/auto_click.png"); 21 | protected Player player = Minecraft.getInstance().player; 22 | protected SpeedSlider slider; 23 | 24 | public ClickMachineScreen(ClickMachineMenu container, Inventory inv, Component name) { 25 | super(container, inv, name); 26 | this.imageWidth = 176; 27 | this.imageHeight = 196; 28 | } 29 | 30 | @Override 31 | public void init() { 32 | super.init(); 33 | int x = this.width / 2 - this.imageWidth / 2 + 30; 34 | int y = this.height / 2 - this.imageHeight / 2 + 26; 35 | this.slider = this.addRenderableWidget(new SpeedSlider(this, x, y, 100, 20)); 36 | this.addRenderableWidget(new ClickerCheckboxButton(this, x, y + 22, 20, 20, Component.translatable("gui.clickmachine.sneaking"), 3, this.menu::isSneaking)); 37 | this.addRenderableWidget(new ClickerCheckboxButton(this, x, y + 44, 20, 20, Component.translatable("gui.clickmachine.right_click"), 4, this.menu::isRightClicking)); 38 | this.menu.addDataListener(this); 39 | } 40 | 41 | @Override 42 | public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { 43 | return this.getFocused() != null && this.isDragging() && button == 0 ? this.getFocused().mouseDragged(mouseX, mouseY, button, dragX, dragY) : super.mouseDragged(mouseX, mouseY, button, dragX, dragY); 44 | } 45 | 46 | @Override 47 | protected void renderLabels(GuiGraphics gfx, int mouseX, int mouseY) { 48 | gfx.drawString(font, this.getNarrationMessage(), 8, 6, 4210752, false); 49 | gfx.drawString(font, this.player.getInventory().getDisplayName().getString(), 8, this.imageHeight - 96 + 2, 4210752, false); 50 | } 51 | 52 | @Override 53 | protected void renderBg(GuiGraphics gfx, float partialTicks, int mouseX, int mouseY) { 54 | int i = (this.width - this.imageWidth) / 2; 55 | int j = (this.height - this.imageHeight) / 2; 56 | gfx.blit(GUI_TEXTURE, i, j, 0, 0, this.imageWidth, this.imageHeight); 57 | int x = i + 150; 58 | int y = j + 26; 59 | gfx.blit(GUI_TEXTURE, x, y, this.imageWidth + 21, 0, 21, 64); 60 | if (ClickMachineConfig.usesRF) { 61 | int maxP = ClickMachineConfig.maxPowerStorage; 62 | double p = this.menu.getEnergy(); 63 | double ratio = p / maxP; 64 | gfx.blit(GUI_TEXTURE, x, y, this.imageWidth, 0, 21, 64 - (int) (ratio * 64)); 65 | } 66 | else { 67 | ClientUtil.colorBlit(gfx.pose(), x + 1, y + 1, this.imageWidth + 43, 1, 19, 62, 0xFF000000 | colors[(int) ((partialTicks + Minecraft.getInstance().player.tickCount / 0.5F) % colors.length)]); 68 | } 69 | } 70 | 71 | @Override 72 | protected void renderTooltip(GuiGraphics gfx, int x, int y) { 73 | super.renderTooltip(gfx, x, y); 74 | if (this.isHovering(150, 26, 21, 64, x, y)) { 75 | if (ClickMachineConfig.usesRF) { 76 | List comps = new ArrayList<>(2); 77 | comps.add(Component.translatable("gui.clickmachine.power", this.menu.getEnergy(), ClickMachineConfig.maxPowerStorage)); 78 | comps.add(Component.translatable("gui.clickmachine.power.usage", ClickMachineConfig.powerPerSpeed[this.menu.getSpeedIdx()])); 79 | gfx.renderComponentTooltip(font, comps, x, y); 80 | } 81 | else { 82 | List comps = new ArrayList<>(1); 83 | comps.add(Component.translatable("gui.clickmachine.rainbow_magic")); 84 | gfx.renderComponentTooltip(font, comps, x, y); 85 | } 86 | } 87 | } 88 | 89 | static int[] colors = { 0xffff00, 0xffff06, 0xffff0c, 0xffff12, 0xffff18, 0xffff1e, 0xffff24, 0xffff2a, 0xffff30, 0xffff36, 0xffff3c, 0xffff42, 0xffff48, 0xffff4e, 0xffff54, 0xffff5a, 0xffff60, 0xffff66, 0xffff6c, 0xffff72, 90 | 0xffff78, 0xffff7e, 0xffff84, 0xffff8a, 0xffff90, 0xffff96, 0xffff9c, 0xffffa2, 0xffffa8, 0xffffae, 0xffffb4, 0xffffba, 0xffffc0, 0xffffc6, 0xffffcc, 0xffffd2, 0xffffd8, 0xffffde, 0xffffe4, 0xffffea, 0xfffff0, 0xfffff6, 91 | 0xfffffc, 0xfffcff, 0xfff6ff, 0xfff0ff, 0xffeaff, 0xffe4ff, 0xffdeff, 0xffd8ff, 0xffd2ff, 0xffccff, 0xffc6ff, 0xffc0ff, 0xffbaff, 0xffb4ff, 0xffaeff, 0xffa8ff, 0xffa2ff, 0xff9cff, 0xff96ff, 0xff90ff, 0xff8aff, 0xff84ff, 92 | 0xff7eff, 0xff78ff, 0xff72ff, 0xff6cff, 0xff66ff, 0xff60ff, 0xff5aff, 0xff54ff, 0xff4eff, 0xff48ff, 0xff42ff, 0xff3cff, 0xff36ff, 0xff30ff, 0xff2aff, 0xff24ff, 0xff1eff, 0xff18ff, 0xff12ff, 0xff0cff, 0xff06ff, 0xff00ff, 93 | 0x6ff00ff, 0xbff00ff, 0x12ff00ff, 0x17ff00ff, 0x1eff00ff, 0x23ff00ff, 0x2aff00ff, 0x2fff00ff, 0x36ff00ff, 0x3bff00ff, 0x42ff00ff, 0x47ff00ff, 0x4eff00ff, 0x53ff00ff, 0x5aff00ff, 0x5fff00ff, 0x66ff00ff, 0x6bff00ff, 0x72ff00ff, 94 | 0x77ff00ff, 0x7eff00ff, 0x83ff00ff, 0x8aff00ff, 0x8fff00ff, 0x96ff00ff, 0x9bff00ff, 0xa2ff00ff, 0xa7ff00ff, 0xaeff00ff, 0xb3ff00ff, 0xbaff00ff, 0xbfff00ff, 0xc6ff00ff, 0xcbff00ff, 0xd2ff00ff, 0xd7ff00ff, 0xdeff00ff, 0xe3ff00ff, 95 | 0xeaff00ff, 0xefff00ff, 0xf6ff00ff, 0xfbff00ff, 0xffff00fc, 0xffff00f6, 0xffff00f0, 0xffff00ea, 0xffff00e4, 0xffff00de, 0xffff00d8, 0xffff00d2, 0xffff00cc, 0xffff00c6, 0xffff00c0, 0xffff00ba, 0xffff00b4, 0xffff00ae, 0xffff00a8, 96 | 0xffff00a2, 0xffff009c, 0xffff0096, 0xffff0090, 0xffff008a, 0xffff0084, 0xffff007e, 0xffff0078, 0xffff0072, 0xffff006c, 0xffff0066, 0xffff0060, 0xffff005a, 0xffff0054, 0xffff004e, 0xffff0048, 0xffff0042, 0xffff003c, 0xffff0036, 97 | 0xffff0030, 0xffff002a, 0xffff0024, 0xffff001e, 0xffff0018, 0xffff0012, 0xffff000c, 0xffff0006, 0xffff0000, 0xffff0600, 0xffff0c00, 0xffff1100, 0xffff1700, 0xffff1e00, 0xffff2400, 0xffff2a00, 0xffff2f00, 0xffff3600, 0xffff3c00, 98 | 0xffff4100, 0xffff4700, 0xffff4e00, 0xffff5400, 0xffff5a00, 0xffff5f00, 0xffff6600, 0xffff6c00, 0xffff7100, 0xffff7700, 0xffff7e00, 0xffff8400, 0xffff8a00, 0xffff8f00, 0xffff9600, 0xffff9c00, 0xffffa100, 0xffffa700, 0xffffae00, 99 | 0xffffb400, 0xffffba00, 0xffffbf00, 0xffffc600, 0xffffcc00, 0xffffd100, 0xffffd700, 0xffffde00, 0xffffe400, 0xffffea00, 0xffffef00, 0xfffff600, 0xfffffc00, 0xfdffff00, 0xf7ffff00, 0xf0ffff00, 0xeaffff00, 0xe4ffff00, 0xdfffff00, 100 | 0xd8ffff00, 0xd2ffff00, 0xcdffff00, 0xc7ffff00, 0xc0ffff00, 0xbaffff00, 0xb4ffff00, 0xafffff00, 0xa8ffff00, 0xa2ffff00, 0x9dffff00, 0x97ffff00, 0x90ffff00, 0x8affff00, 0x84ffff00, 0x7fffff00, 0x78ffff00, 0x72ffff00, 0x6dffff00, 101 | 0x67ffff00, 0x60ffff00, 0x5affff00, 0x54ffff00, 0x4fffff00, 0x48ffff00, 0x42ffff00, 0x3dffff00, 0x37ffff00, 0x30ffff00, 0x2affff00, 0x24ffff00, 0x1fffff00, 0x18ffff00, 0x12ffff00, 0xdffff00, 0x7ffff00 }; 102 | 103 | @Override 104 | public void dataUpdated(int id, int value) { 105 | if (id == 1) { 106 | this.slider.setValue(value); 107 | } 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/block/ClickMachineTile.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.block; 2 | 3 | import java.util.UUID; 4 | import java.util.function.Consumer; 5 | 6 | import com.mojang.authlib.GameProfile; 7 | 8 | import dev.shadowsoffire.clickmachine.ClickMachine; 9 | import dev.shadowsoffire.clickmachine.ClickMachineConfig; 10 | import dev.shadowsoffire.clickmachine.util.FakePlayerUtil; 11 | import dev.shadowsoffire.clickmachine.util.FakePlayerUtil.UsefulFakePlayer; 12 | import dev.shadowsoffire.placebo.block_entity.TickingBlockEntity; 13 | import dev.shadowsoffire.placebo.cap.ModifiableEnergyStorage; 14 | import dev.shadowsoffire.placebo.menu.SimpleDataSlots; 15 | import dev.shadowsoffire.placebo.menu.SimpleDataSlots.IDataAutoRegister; 16 | import net.minecraft.core.BlockPos; 17 | import net.minecraft.core.Direction; 18 | import net.minecraft.core.HolderLookup; 19 | import net.minecraft.core.HolderLookup.Provider; 20 | import net.minecraft.nbt.CompoundTag; 21 | import net.minecraft.network.Connection; 22 | import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; 23 | import net.minecraft.world.entity.player.Player; 24 | import net.minecraft.world.inventory.DataSlot; 25 | import net.minecraft.world.item.ItemStack; 26 | import net.minecraft.world.level.Level; 27 | import net.minecraft.world.level.block.entity.BlockEntity; 28 | import net.minecraft.world.level.block.state.BlockState; 29 | import net.neoforged.neoforge.energy.IEnergyStorage; 30 | import net.neoforged.neoforge.items.IItemHandler; 31 | import net.neoforged.neoforge.items.ItemStackHandler; 32 | 33 | public class ClickMachineTile extends BlockEntity implements Consumer, TickingBlockEntity, IDataAutoRegister { 34 | 35 | public static final GameProfile DEFAULT_CLICKER = new GameProfile(UUID.fromString("36f373ac-29ef-4150-b664-e7e6006efcd8"), "[The Click Machine]"); 36 | 37 | protected final SimpleDataSlots data = new SimpleDataSlots(); 38 | 39 | protected ItemStackHandler held; 40 | protected ModifiableEnergyStorage power = new ModifiableEnergyStorage(ClickMachineConfig.maxPowerStorage); 41 | protected int speedIdx = 0; 42 | protected boolean sneak = false; 43 | protected boolean rightClick = true; 44 | protected int counter = 0; 45 | 46 | protected transient GameProfile profile; 47 | protected transient UsefulFakePlayer player; 48 | 49 | public ClickMachineTile(BlockPos pos, BlockState state) { 50 | super(ClickMachine.CLICK_MACHINE_TILE, pos, state); 51 | this.held = new ItemStackHandler(1){ 52 | @Override 53 | public boolean isItemValid(int slot, ItemStack stack) { 54 | return !ClickMachineConfig.blacklistedItems.contains(stack.getItem()); 55 | } 56 | }; 57 | this.data.addEnergy(this.power); 58 | this.data.addData(() -> this.speedIdx, v -> this.speedIdx = v); 59 | this.data.addData(() -> this.sneak, v -> this.sneak = v); 60 | this.data.addData(() -> this.rightClick, v -> this.rightClick = v); 61 | } 62 | 63 | @Override 64 | public void serverTick(Level level, BlockPos pos, BlockState state) { 65 | if (this.player == null) { 66 | this.player = FakePlayerUtil.createPlayer(level, this.profile != null ? this.profile : DEFAULT_CLICKER); 67 | } 68 | if (!level.hasNeighborSignal(this.worldPosition)) { 69 | int use = ClickMachineConfig.usesRF ? ClickMachineConfig.powerPerSpeed[this.getSpeedIndex()] : 0; 70 | if (this.power.extractEnergy(use, true) == use) { 71 | this.power.extractEnergy(use, false); 72 | if (this.counter++ % this.getSpeed() == 0) { 73 | Direction facing = level.getBlockState(this.worldPosition).getValue(ClickMachineBlock.FACING); 74 | FakePlayerUtil.setupFakePlayerForUse(this.getPlayer(), this.worldPosition, facing, this.held.getStackInSlot(0).copy(), this.sneak); 75 | ItemStack result = this.held.getStackInSlot(0); 76 | if (this.rightClick) result = FakePlayerUtil.rightClickInDirection(this.getPlayer(), this.level, this.worldPosition, facing, level.getBlockState(this.worldPosition)); 77 | else result = FakePlayerUtil.leftClickInDirection(this.getPlayer(), this.level, this.worldPosition, facing, level.getBlockState(this.worldPosition)); 78 | FakePlayerUtil.cleanupFakePlayerFromUse(this.getPlayer(), result, this.held.getStackInSlot(0), this); 79 | this.setChanged(); 80 | } 81 | } 82 | if (!state.getValue(ClickMachineBlock.ACTIVE)) { 83 | level.setBlock(this.worldPosition, state.setValue(ClickMachineBlock.ACTIVE, true), 2); 84 | } 85 | } 86 | else { 87 | if (state.getValue(ClickMachineBlock.ACTIVE)) { 88 | level.setBlock(this.worldPosition, state.setValue(ClickMachineBlock.ACTIVE, false), 2); 89 | } 90 | } 91 | 92 | this.player.getCooldowns().tick(); 93 | } 94 | 95 | public void setPlayer(Player player) { 96 | this.profile = player.getGameProfile(); 97 | this.setChanged(); 98 | } 99 | 100 | UsefulFakePlayer getPlayer() { 101 | return this.player; 102 | } 103 | 104 | public IItemHandler getHandler() { 105 | return this.held; 106 | } 107 | 108 | public IEnergyStorage getEnergy() { 109 | return this.power; 110 | } 111 | 112 | public int getSpeed() { 113 | return ClickMachineConfig.speeds[this.getSpeedIndex()]; 114 | } 115 | 116 | public int getSpeedIndex() { 117 | return this.speedIdx; 118 | } 119 | 120 | public void setSpeedIndex(int speedIdx) { 121 | this.speedIdx = speedIdx; 122 | this.setChanged(); 123 | } 124 | 125 | public boolean isSneaking() { 126 | return this.sneak; 127 | } 128 | 129 | public void setSneaking(boolean sneak) { 130 | this.sneak = sneak; 131 | this.setChanged(); 132 | } 133 | 134 | public boolean isRightClicking() { 135 | return this.rightClick; 136 | } 137 | 138 | public void setRightClicking(boolean rightClick) { 139 | this.rightClick = rightClick; 140 | this.setChanged(); 141 | } 142 | 143 | static final String tagUUID = "uuid"; 144 | static final String tagName = "name"; 145 | static final String tagCounter = "counter"; 146 | static final String tagSpeed = "speed_index"; 147 | static final String tagSneak = "sneak"; 148 | static final String tagRightClick = "right_click"; 149 | static final String tagHandler = "inv"; 150 | static final String tagEnergy = "fe"; 151 | 152 | @Override 153 | protected void saveAdditional(CompoundTag tag, HolderLookup.Provider regs) { 154 | super.saveAdditional(tag, regs); 155 | if (this.profile != null) { 156 | tag.putUUID(tagUUID, this.profile.getId()); 157 | tag.putString(tagName, this.profile.getName()); 158 | } 159 | tag.put(tagHandler, this.held.serializeNBT(regs)); 160 | tag.putInt(tagCounter, this.counter % this.getSpeed()); 161 | this.writeSyncData(tag); 162 | } 163 | 164 | CompoundTag writeSyncData(CompoundTag tag) { 165 | tag.putInt(tagSpeed, this.getSpeedIndex()); 166 | tag.putBoolean(tagSneak, this.sneak); 167 | tag.putBoolean(tagRightClick, this.rightClick); 168 | tag.putInt(tagEnergy, this.power.getEnergyStored()); 169 | return tag; 170 | } 171 | 172 | void readSyncData(CompoundTag tag) { 173 | this.setSpeedIndex(tag.getInt(tagSpeed)); 174 | this.sneak = tag.getBoolean(tagSneak); 175 | this.rightClick = tag.getBoolean(tagRightClick); 176 | this.setPower(tag.getInt(tagEnergy)); 177 | } 178 | 179 | @Override 180 | protected void loadAdditional(CompoundTag tag, Provider regs) { 181 | super.loadAdditional(tag, regs); 182 | if (tag.contains(tagUUID) && tag.contains(tagName)) this.profile = new GameProfile(tag.getUUID(tagUUID), tag.getString(tagName)); 183 | if (tag.contains(tagHandler)) this.held.deserializeNBT(regs, tag.getCompound(tagHandler)); 184 | this.counter = tag.getInt(tagCounter); 185 | this.readSyncData(tag); 186 | } 187 | 188 | @Override 189 | public ClientboundBlockEntityDataPacket getUpdatePacket() { 190 | return ClientboundBlockEntityDataPacket.create(this, (be, regs) -> ((ClickMachineTile) be).writeSyncData(new CompoundTag())); 191 | } 192 | 193 | @Override 194 | public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt, HolderLookup.Provider regs) { 195 | this.readSyncData(pkt.getTag()); 196 | } 197 | 198 | public int getPower() { 199 | return this.power.getEnergyStored(); 200 | } 201 | 202 | public void setPower(int energy) { 203 | this.power.extractEnergy(this.power.getMaxEnergyStored(), false); 204 | this.power.receiveEnergy(energy, false); 205 | this.setChanged(); 206 | } 207 | 208 | @Override 209 | public void accept(ItemStack s) { 210 | this.held.setStackInSlot(0, s); 211 | } 212 | 213 | @Override 214 | public void registerSlots(Consumer consumer) { 215 | this.data.register(consumer); 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/dev/shadowsoffire/clickmachine/util/FakePlayerUtil.java: -------------------------------------------------------------------------------- 1 | package dev.shadowsoffire.clickmachine.util; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import java.util.OptionalInt; 6 | import java.util.function.Consumer; 7 | 8 | import javax.annotation.Nullable; 9 | 10 | import com.mojang.authlib.GameProfile; 11 | 12 | import dev.shadowsoffire.clickmachine.block.ClickMachineTile; 13 | import net.minecraft.core.BlockPos; 14 | import net.minecraft.core.Direction; 15 | import net.minecraft.core.Direction.Axis; 16 | import net.minecraft.core.Direction.AxisDirection; 17 | import net.minecraft.core.Vec3i; 18 | import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket.Action; 19 | import net.minecraft.server.level.ServerLevel; 20 | import net.minecraft.world.Container; 21 | import net.minecraft.world.InteractionHand; 22 | import net.minecraft.world.InteractionResult; 23 | import net.minecraft.world.MenuProvider; 24 | import net.minecraft.world.entity.Entity; 25 | import net.minecraft.world.entity.EntitySelector; 26 | import net.minecraft.world.entity.EquipmentSlot; 27 | import net.minecraft.world.entity.ExperienceOrb; 28 | import net.minecraft.world.entity.ai.attributes.AttributeInstance; 29 | import net.minecraft.world.entity.ai.attributes.Attributes; 30 | import net.minecraft.world.entity.animal.horse.AbstractHorse; 31 | import net.minecraft.world.entity.item.ItemEntity; 32 | import net.minecraft.world.entity.projectile.Arrow; 33 | import net.minecraft.world.item.ItemStack; 34 | import net.minecraft.world.item.component.ItemAttributeModifiers; 35 | import net.minecraft.world.level.ClipContext; 36 | import net.minecraft.world.level.ClipContext.Block; 37 | import net.minecraft.world.level.ClipContext.Fluid; 38 | import net.minecraft.world.level.Level; 39 | import net.minecraft.world.level.block.state.BlockState; 40 | import net.minecraft.world.level.portal.DimensionTransition; 41 | import net.minecraft.world.phys.AABB; 42 | import net.minecraft.world.phys.BlockHitResult; 43 | import net.minecraft.world.phys.EntityHitResult; 44 | import net.minecraft.world.phys.HitResult; 45 | import net.minecraft.world.phys.Vec3; 46 | import net.neoforged.neoforge.common.CommonHooks; 47 | import net.neoforged.neoforge.common.util.FakePlayer; 48 | 49 | public class FakePlayerUtil { 50 | 51 | public static class UsefulFakePlayer extends FakePlayer { 52 | 53 | public UsefulFakePlayer(Level world, GameProfile name) { 54 | super((ServerLevel) world, name); 55 | } 56 | 57 | @Override 58 | public void initInventoryMenu() {} 59 | 60 | @Override 61 | public void openHorseInventory(AbstractHorse horse, Container container) {} 62 | 63 | @Override 64 | public OptionalInt openMenu(MenuProvider p_9033_) { 65 | return OptionalInt.empty(); 66 | } 67 | 68 | @Override 69 | public float getAttackStrengthScale(float adjustTicks) { 70 | return 1; // Prevent the attack strength from always being 0.03 due to not ticking. 71 | } 72 | 73 | @Override 74 | public Entity changeDimension(DimensionTransition transition) { 75 | return null; 76 | } 77 | } 78 | 79 | /** 80 | * Creates a new UsefulFakePlayer. Each {@link ClickMachineTile} needs its own. 81 | */ 82 | public static UsefulFakePlayer createPlayer(Level world, GameProfile profile) { 83 | return new UsefulFakePlayer(world, profile); 84 | } 85 | 86 | /** 87 | * Sets up for a fake player to be usable to right click things. This player will be put at the center of the using side. 88 | * 89 | * @param player The player. 90 | * @param pos The position of the using tile entity. 91 | * @param direction The direction to use in. 92 | * @param toHold The stack the player will be using. Should probably come from an ItemStackHandler or similar. 93 | */ 94 | public static void setupFakePlayerForUse(UsefulFakePlayer player, BlockPos pos, Direction direction, ItemStack toHold, boolean sneaking) { 95 | player.getInventory().items.set(player.getInventory().selected, toHold); 96 | float pitch = direction == Direction.UP ? -90 : direction == Direction.DOWN ? 90 : 0; 97 | float yaw = direction == Direction.SOUTH ? 0 : direction == Direction.WEST ? 90 : direction == Direction.NORTH ? 180 : -90; 98 | Vec3i sideVec = direction.getNormal(); 99 | Axis a = direction.getAxis(); 100 | AxisDirection ad = direction.getAxisDirection(); 101 | double x = a == Axis.X && ad == AxisDirection.NEGATIVE ? -.5 : .5 + sideVec.getX() / 1.9D; 102 | double y = 0.5 + sideVec.getY() / 1.9D; 103 | double z = a == Axis.Z && ad == AxisDirection.NEGATIVE ? -.5 : .5 + sideVec.getZ() / 1.9D; 104 | player.moveTo(pos.getX() + x, pos.getY() + y - player.getEyeHeight(), pos.getZ() + z, yaw, pitch); 105 | if (!toHold.isEmpty()) { 106 | ItemAttributeModifiers modifiers = toHold.getAttributeModifiers(); 107 | modifiers.forEach(EquipmentSlot.MAINHAND, (attr, modif) -> { 108 | AttributeInstance inst = player.getAttribute(attr); 109 | if (inst != null) { 110 | inst.addOrUpdateTransientModifier(modif); 111 | } 112 | }); 113 | } 114 | player.setShiftKeyDown(sneaking); 115 | } 116 | 117 | /** 118 | * Cleans up the fake player after use. 119 | * 120 | * @param player The player. 121 | * @param resultStack The stack that was returned from right/leftClickInDirection. 122 | * @param oldStack The previous stack, from before use. 123 | */ 124 | public static void cleanupFakePlayerFromUse(UsefulFakePlayer player, ItemStack resultStack, ItemStack oldStack, Consumer stackCallback) { 125 | if (!oldStack.isEmpty()) { 126 | ItemAttributeModifiers modifiers = oldStack.getAttributeModifiers(); 127 | modifiers.forEach(EquipmentSlot.MAINHAND, (attr, modif) -> { 128 | AttributeInstance inst = player.getAttribute(attr); 129 | if (inst != null) { 130 | inst.removeModifier(modif); 131 | } 132 | }); 133 | } 134 | player.getInventory().items.set(player.getInventory().selected, ItemStack.EMPTY); 135 | stackCallback.accept(resultStack); 136 | if (!player.getInventory().isEmpty()) player.getInventory().dropAll(); 137 | player.setShiftKeyDown(false); 138 | } 139 | 140 | /** 141 | * Uses whatever the player happens to be holding in the given direction. 142 | * 143 | * @param player The player. 144 | * @param world The world of the calling tile entity. It may be a bad idea to use {@link FakePlayer#getEntityWorld()}. 145 | * @param pos The pos of the calling tile entity. 146 | * @param side The direction to use in. 147 | * @param sourceState The state of the calling tile entity, so we don't click ourselves. 148 | * @return The remainder of whatever the player was holding. This should be set back into the tile's stack handler or similar. 149 | */ 150 | public static ItemStack rightClickInDirection(UsefulFakePlayer player, Level world, BlockPos pos, Direction side, BlockState sourceState) { 151 | HitResult toUse = rayTrace(player, world, player.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE)); 152 | if (toUse == null) return player.getMainHandItem(); 153 | 154 | ItemStack itemstack = player.getMainHandItem(); 155 | if (toUse.getType() == HitResult.Type.ENTITY) { 156 | if (processUseEntity(player, world, ((EntityHitResult) toUse).getEntity(), toUse, InteractionType.INTERACT_AT)) return player.getMainHandItem(); 157 | else if (processUseEntity(player, world, ((EntityHitResult) toUse).getEntity(), null, InteractionType.INTERACT)) return player.getMainHandItem(); 158 | } 159 | else if (toUse.getType() == HitResult.Type.BLOCK) { 160 | BlockPos blockpos = ((BlockHitResult) toUse).getBlockPos(); 161 | BlockState state = world.getBlockState(blockpos); 162 | if (state != sourceState && !state.isAir()) { 163 | InteractionResult type = player.gameMode.useItemOn(player, world, itemstack, InteractionHand.MAIN_HAND, (BlockHitResult) toUse); 164 | if (type == InteractionResult.SUCCESS) return player.getMainHandItem(); 165 | } 166 | } 167 | 168 | if (toUse == null || toUse.getType() == HitResult.Type.MISS) { 169 | for (int i = 1; i <= 5; i++) { 170 | BlockState state = world.getBlockState(pos.relative(side, i)); 171 | if (state != sourceState && !state.isAir()) { 172 | player.gameMode.useItemOn(player, world, itemstack, InteractionHand.MAIN_HAND, (BlockHitResult) toUse); 173 | return player.getMainHandItem(); 174 | } 175 | } 176 | } 177 | 178 | if (itemstack.isEmpty() && (toUse == null || toUse.getType() == HitResult.Type.MISS)) CommonHooks.onEmptyClick(player, InteractionHand.MAIN_HAND); 179 | if (!itemstack.isEmpty()) player.gameMode.useItem(player, world, itemstack, InteractionHand.MAIN_HAND); 180 | return player.getMainHandItem(); 181 | } 182 | 183 | /** 184 | * Attacks with whatever the player happens to be holding in the given direction. 185 | * 186 | * @param player The player. 187 | * @param world The world of the calling tile entity. It may be a bad idea to use {@link FakePlayer#getEntityWorld()}. 188 | * @param pos The pos of the calling tile entity. 189 | * @param side The direction to attack in. 190 | * @param sourceState The state of the calling tile entity, so we don't click ourselves. 191 | * @return The remainder of whatever the player was holding. This should be set back into the tile's stack handler or similar. 192 | */ 193 | public static ItemStack leftClickInDirection(UsefulFakePlayer player, Level world, BlockPos pos, Direction side, BlockState sourceState) { 194 | HitResult toUse = rayTrace(player, world, player.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE)); 195 | if (toUse == null) return player.getMainHandItem(); 196 | 197 | if (toUse.getType() == HitResult.Type.ENTITY) { 198 | if (processUseEntity(player, world, ((EntityHitResult) toUse).getEntity(), null, InteractionType.ATTACK)) return player.getMainHandItem(); 199 | } 200 | else if (toUse.getType() == HitResult.Type.BLOCK) { 201 | BlockPos blockpos = ((BlockHitResult) toUse).getBlockPos(); 202 | BlockState state = world.getBlockState(blockpos); 203 | if (state != sourceState && !state.isAir()) { 204 | player.gameMode.handleBlockBreakAction(blockpos, Action.START_DESTROY_BLOCK, ((BlockHitResult) toUse).getDirection(), player.level().getMaxBuildHeight(), 0); 205 | return player.getMainHandItem(); 206 | } 207 | } 208 | 209 | if (toUse == null || toUse.getType() == HitResult.Type.MISS) { 210 | for (int i = 1; i <= 5; i++) { 211 | BlockState state = world.getBlockState(pos.relative(side, i)); 212 | if (state != sourceState && !state.isAir()) { 213 | player.gameMode.handleBlockBreakAction(pos.relative(side, i), Action.START_DESTROY_BLOCK, side.getOpposite(), player.level().getMaxBuildHeight(), 0); 214 | return player.getMainHandItem(); 215 | } 216 | } 217 | } 218 | 219 | return player.getMainHandItem(); 220 | } 221 | 222 | /** 223 | * Traces for an entity. 224 | * 225 | * @param player The player. 226 | * @param world The world of the calling tile entity. 227 | * @return A ray trace result that will likely be of type entity, but may be type block, or null. 228 | */ 229 | public static HitResult traceEntities(UsefulFakePlayer player, Vec3 base, Vec3 target, Level world) { 230 | Entity pointedEntity = null; 231 | HitResult result = null; 232 | Vec3 vec3d3 = null; 233 | AABB search = new AABB(base.x, base.y, base.z, target.x, target.y, target.z).inflate(.5, .5, .5); 234 | List list = world.getEntities(player, search, entity -> EntitySelector.NO_SPECTATORS.test(entity) && entity != null && entity.isPickable()); 235 | double d2 = 5; 236 | 237 | for (int j = 0; j < list.size(); ++j) { 238 | Entity entity1 = list.get(j); 239 | 240 | AABB aabb = entity1.getBoundingBox().inflate(entity1.getPickRadius()); 241 | Optional optVec = aabb.clip(base, target); 242 | 243 | if (aabb.contains(base)) { 244 | if (d2 >= 0.0D) { 245 | pointedEntity = entity1; 246 | vec3d3 = optVec.orElse(base); 247 | d2 = 0.0D; 248 | } 249 | } 250 | else if (optVec.isPresent()) { 251 | double d3 = base.distanceTo(optVec.get()); 252 | 253 | if (d3 < d2 || d2 == 0.0D) { 254 | if (entity1.getRootVehicle() == player.getRootVehicle() && !entity1.canRiderInteract()) { 255 | if (d2 == 0.0D) { 256 | pointedEntity = entity1; 257 | vec3d3 = optVec.get(); 258 | } 259 | } 260 | else { 261 | pointedEntity = entity1; 262 | vec3d3 = optVec.get(); 263 | d2 = d3; 264 | } 265 | } 266 | } 267 | } 268 | 269 | if (pointedEntity != null && base.distanceTo(vec3d3) > 5) { 270 | pointedEntity = null; 271 | result = BlockHitResult.miss(vec3d3, null, BlockPos.containing(vec3d3)); 272 | } 273 | 274 | if (pointedEntity != null) { 275 | result = new EntityHitResult(pointedEntity, vec3d3); 276 | } 277 | 278 | return result; 279 | } 280 | 281 | /** 282 | * Processes the using of an entity from the server side. 283 | * 284 | * @param player The player. 285 | * @param world The world of the calling tile entity. 286 | * @param entity The entity to interact with. 287 | * @param result The actual ray trace result, only necessary if using {@link CUseEntityPacket.Action#INTERACT_AT} 288 | * @param action The type of interaction to perform. 289 | * @return If the entity was used. 290 | */ 291 | public static boolean processUseEntity(UsefulFakePlayer player, Level world, Entity entity, @Nullable HitResult result, InteractionType action) { 292 | if (entity != null) { 293 | if (player.distanceToSqr(entity) < 36) { 294 | if (action == InteractionType.INTERACT) { 295 | return player.interactOn(entity, InteractionHand.MAIN_HAND) == InteractionResult.SUCCESS; 296 | } 297 | else if (action == InteractionType.INTERACT_AT) { 298 | if (CommonHooks.onInteractEntityAt(player, entity, result.getLocation(), InteractionHand.MAIN_HAND) != null) return false; 299 | return entity.interactAt(player, result.getLocation(), InteractionHand.MAIN_HAND) == InteractionResult.SUCCESS; 300 | } 301 | else if (action == InteractionType.ATTACK) { 302 | if (entity instanceof ItemEntity || entity instanceof ExperienceOrb || entity instanceof Arrow || entity == player) return false; 303 | player.attack(entity); 304 | return true; 305 | } 306 | } 307 | } 308 | return false; 309 | } 310 | 311 | /** 312 | * Util to perform a raytrace for what the fake player is looking at. 313 | */ 314 | public static HitResult rayTrace(UsefulFakePlayer player, Level level, double reachDist) { 315 | Vec3 base = new Vec3(player.getX(), player.getEyeY(), player.getZ()); 316 | Vec3 look = player.getLookAngle(); 317 | Vec3 target = base.add(look.x * reachDist, look.y * reachDist, look.z * reachDist); 318 | HitResult trace = level.clip(new ClipContext(base, target, Block.OUTLINE, Fluid.NONE, player)); 319 | HitResult traceEntity = traceEntities(player, base, target, level); 320 | HitResult toUse = trace == null ? traceEntity : trace; 321 | 322 | if (trace != null && traceEntity != null) { 323 | double d1 = trace.getLocation().distanceTo(base); 324 | double d2 = traceEntity.getLocation().distanceTo(base); 325 | toUse = traceEntity.getType() == HitResult.Type.ENTITY && d1 > d2 ? traceEntity : trace; 326 | } 327 | 328 | return toUse; 329 | } 330 | 331 | public static enum InteractionType { 332 | INTERACT, 333 | INTERACT_AT, 334 | ATTACK; 335 | } 336 | 337 | } 338 | --------------------------------------------------------------------------------