From 1f2c68181ea1499492a45e6b33abb3ef6833a850 Mon Sep 17 00:00:00 2001 From: Cameron Reed Date: Sun, 4 Dec 2022 00:27:22 -0700 Subject: [PATCH] Initial Commit --- .gitignore | 119 +++++++ LICENSE | 21 ++ build.gradle | 86 +++++ gradle.properties | 14 + gradle/wrapper/gradle-wrapper.properties | 1 + settings.gradle | 9 + .../java/cmods/haxxor/client/AutoFarmer.java | 322 ++++++++++++++++++ .../cmods/haxxor/client/HaxxorClient.java | 53 +++ .../cmods/haxxor/client/HaxxorOptions.java | 90 +++++ .../cmods/haxxor/client/ModMenuConfig.java | 13 + .../cmods/haxxor/client/ui/Constants.java | 14 + .../haxxor/client/ui/CropSelectScreen.java | 80 +++++ .../haxxor/client/ui/CycleButtonWidget.java | 41 +++ .../haxxor/client/ui/FarmerOptionsScreen.java | 115 +++++++ .../haxxor/client/ui/HaxxorOptionsScreen.java | 46 +++ .../client/ui/ToggleEnableButtonWidget.java | 51 +++ .../java/cmods/haxxor/mixin/HudMixin.java | 60 ++++ .../java/cmods/haxxor/mixin/OptionsMixin.java | 29 ++ .../java/cmods/haxxor/mixin/PauseMixin.java | 30 ++ src/main/resources/assets/haxxor/icon.png | Bin 0 -> 1215 bytes .../resources/assets/haxxor/lang/en_us.json | 26 ++ src/main/resources/fabric.mod.json | 30 ++ src/main/resources/haxxor.mixins.json | 16 + 23 files changed, 1266 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 settings.gradle create mode 100644 src/main/java/cmods/haxxor/client/AutoFarmer.java create mode 100644 src/main/java/cmods/haxxor/client/HaxxorClient.java create mode 100644 src/main/java/cmods/haxxor/client/HaxxorOptions.java create mode 100644 src/main/java/cmods/haxxor/client/ModMenuConfig.java create mode 100644 src/main/java/cmods/haxxor/client/ui/Constants.java create mode 100644 src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java create mode 100644 src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java create mode 100644 src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java create mode 100644 src/main/java/cmods/haxxor/client/ui/HaxxorOptionsScreen.java create mode 100644 src/main/java/cmods/haxxor/client/ui/ToggleEnableButtonWidget.java create mode 100644 src/main/java/cmods/haxxor/mixin/HudMixin.java create mode 100644 src/main/java/cmods/haxxor/mixin/OptionsMixin.java create mode 100644 src/main/java/cmods/haxxor/mixin/PauseMixin.java create mode 100644 src/main/resources/assets/haxxor/icon.png create mode 100644 src/main/resources/assets/haxxor/lang/en_us.json create mode 100644 src/main/resources/fabric.mod.json create mode 100644 src/main/resources/haxxor.mixins.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e282f40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ +remappedSrc/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6e8dd17 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 CameronReed + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..bbc6c19 --- /dev/null +++ b/build.gradle @@ -0,0 +1,86 @@ +plugins { + id 'fabric-loom' version '1.0-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. + maven { + name = 'TerraformersMC' + url = 'https://maven.terraformersmc.com/releases' + } +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + modCompileOnly "com.terraformersmc:modmenu:4.1.1" +} + +processResources { + inputs.property "version", project.version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +def targetJavaVersion = 17 +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release = targetJavaVersion + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + archivesBaseName = project.archives_base_name + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}" } + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..8b68162 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,14 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx2G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.19.2 +yarn_mappings=1.19.2+build.28 +loader_version=0.14.10 +# Mod Properties +mod_version=1.0.0 +maven_group=cmods +archives_base_name=haxxor +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.67.1+1.19.2 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..73bb918 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/cmods/haxxor/client/AutoFarmer.java b/src/main/java/cmods/haxxor/client/AutoFarmer.java new file mode 100644 index 0000000..b52ea85 --- /dev/null +++ b/src/main/java/cmods/haxxor/client/AutoFarmer.java @@ -0,0 +1,322 @@ +package cmods.haxxor.client; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.block.*; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket; +import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket; +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; + +import java.util.ArrayList; +import java.util.Objects; + +public class AutoFarmer { // Really just a namespace and needed to separate things into their own files + public static final AutoFarmerCropType[] crops = { + new AutoFarmerCropType(Blocks.WHEAT, Items.WHEAT_SEEDS, GROWTH_PATTERN.BASIC, Blocks.FARMLAND), + new AutoFarmerCropType(Blocks.BEETROOTS, Items.BEETROOT_SEEDS, GROWTH_PATTERN.BASIC, Blocks.FARMLAND), + new AutoFarmerCropType(Blocks.POTATOES, Items.POTATO, GROWTH_PATTERN.BASIC, Blocks.FARMLAND), + new AutoFarmerCropType(Blocks.CARROTS, Items.CARROT, GROWTH_PATTERN.BASIC, Blocks.FARMLAND), + new AutoFarmerCropType(Blocks.NETHER_WART, Items.NETHER_WART, GROWTH_PATTERN.BASIC, Blocks.SOUL_SAND), + new AutoFarmerCropType(Blocks.SUGAR_CANE, null, GROWTH_PATTERN.LEAVE_BASE, null) + }; + + + private enum ACTION_TYPE { + NONE, + BREAK, + PLACE, + PLACE_MAIN_HAND, + PLACE_OFF_HAND + } + + private enum GROWTH_PATTERN { + BASIC, + LEAVE_BASE + } + + private static final ArrayList potentialActions = new ArrayList<>(); + private static final ArrayList queuedActions = new ArrayList<>(); + private static final ArrayList queuedStopPackets = new ArrayList<>(); + private static HaxxorOptions.AutoFarmerOptions options; + + private static final int offHandSlot = 45; + + + public static void init() { + options = HaxxorOptions.getInstance().autoFarmer; + } + + public static boolean canFarm() { + return queuedActions.size() > 0; + } + + public static void tick(MinecraftClient client) { + if (client.player == null) + return; + + if (queuedStopPackets.size() > 0) + sendStopPackets(client); + + scanBlocks(client); + if (options.move_seeds && options.enabled) + adjustInventory(client); + findActions(client); + + if (options.enabled) + run(client); + } + + + private static void run(MinecraftClient client) { + if (client.world == null || client.player == null) + return; + + for (FarmerAction action : queuedActions) { + switch (action.type) { + case BREAK -> breakCrop(client, action.pos); + case PLACE_MAIN_HAND -> plantSeeds(client, action.pos, Hand.MAIN_HAND); + case PLACE_OFF_HAND -> plantSeeds(client, action.pos, Hand.OFF_HAND); + } + } + } + + public static void adjustInventory(MinecraftClient client) { + if (client.player == null || client.world == null) + return; + + boolean item_in_main = false; + boolean item_in_off = false; + + PlayerInventory inventory = client.player.getInventory(); + + for (FarmerAction action: potentialActions) { + if (action.type != ACTION_TYPE.PLACE) + continue; + + BlockState block = client.world.getBlockState(action.pos); + for (int i = 0; i < crops.length; i++) { + int slot = inventory.getSlotWithStack(new ItemStack(crops[i].seed_type)); + if (crops[i].seed_type != null && crops[i].planted_on != null && block.isOf(crops[i].planted_on) && + options.crops_enabled[i] && slot != -1) { + if (!item_in_main) { + if (PlayerInventory.isValidHotbarIndex(slot)) { + selectSlot(client, slot); + } else if (slot < 36) { + moveToHotBar(client, inventory, inventory.getSwappableHotbarSlot(), slot); + } + item_in_main = true; + } else if (!item_in_off && slot < 36 && slot > 8) { + moveToOffHand(client, inventory, slot); + } + } + } + } + } + + private static void moveToOffHand(MinecraftClient client, PlayerInventory inventory, int slot) { + if (client.player == null) + return; + + int[] slots = {slot, offHandSlot}; + ItemStack[] stacks = {inventory.getStack(offHandSlot), inventory.getStack(slot)}; + Int2ObjectMap modifiedItems = new Int2ObjectArrayMap<>(slots, stacks); + + Objects.requireNonNull(client.getNetworkHandler()).sendPacket( + new ClickSlotC2SPacket(0, client.player.playerScreenHandler.getRevision(), slot, 40, + SlotActionType.SWAP, ItemStack.EMPTY, modifiedItems)); + } + + private static void moveToHotBar(MinecraftClient client, PlayerInventory inventory, int hotBarSlot, int slot) { + if (client.player == null) + return; + + int[] slots = {slot, offHandSlot}; + ItemStack[] stacks = {inventory.getStack(offHandSlot), inventory.getStack(slot)}; + Int2ObjectMap modifiedItems = new Int2ObjectArrayMap<>(slots, stacks); + + Objects.requireNonNull(client.getNetworkHandler()).sendPacket( + new ClickSlotC2SPacket(0, client.player.playerScreenHandler.getRevision(), slot, hotBarSlot, + SlotActionType.SWAP, ItemStack.EMPTY, modifiedItems)); + + selectSlot(client, hotBarSlot); + } + + private static void selectSlot(MinecraftClient client, int slot) { + if (PlayerInventory.isValidHotbarIndex(slot) && client.player != null) { + client.player.getInventory().selectedSlot = slot; + Objects.requireNonNull(client.getNetworkHandler()).sendPacket(new UpdateSelectedSlotC2SPacket(slot)); + } + } + + private static void scanBlocks(MinecraftClient client) { + if (client.world == null || client.player == null) + return; + + potentialActions.clear(); + BlockPos playerPos = client.player.getBlockPos(); + + for (int y = options.min_y; y <= options.max_y; y++) { + for (int x = -options.horizontal_reach; x <= options.horizontal_reach; x++) { + for (int z = -options.horizontal_reach; z <= options.horizontal_reach; z++) { + BlockPos pos = playerPos.add(x, y, z); + + ACTION_TYPE action = checkBlock(client, pos); + if (action != ACTION_TYPE.NONE) { + potentialActions.add(new FarmerAction(pos, action)); + } + } + } + } + } + + private static ACTION_TYPE checkBlock(MinecraftClient client, BlockPos pos) { + if (client.world == null || client.player == null) + return ACTION_TYPE.NONE; + + BlockState block = client.world.getBlockState(pos); + + for (int i = 0; i < crops.length; i++) { + AutoFarmerCropType cropType = crops[i]; + + if (cropType.growth_pattern == GROWTH_PATTERN.BASIC && block.isOf(cropType.crop_type) && + options.crops_enabled[i]) { + return isMature(block, cropType.crop_type.getClass()) ? ACTION_TYPE.BREAK : ACTION_TYPE.NONE; + } + + if (cropType.growth_pattern == GROWTH_PATTERN.LEAVE_BASE && block.isOf(cropType.crop_type) && + options.crops_enabled[i]) { + return isSecondBlock(client.world, pos, cropType.crop_type) ? ACTION_TYPE.BREAK : ACTION_TYPE.NONE; + } + + if (cropType.seed_type != null && cropType.planted_on != null && block.isOf(cropType.planted_on) && + client.world.getBlockState(pos.up()).isAir() && options.seeds_enabled[i]) { + return ACTION_TYPE.PLACE; + } + } + + return ACTION_TYPE.NONE; + } + + private static void findActions(MinecraftClient client) { + if (client.world == null || client.player == null) + return; + + queuedActions.clear(); + + for (FarmerAction action: potentialActions) { + if (action.type != ACTION_TYPE.PLACE) { + queuedActions.add(action); + } else { + ACTION_TYPE action_type = holingSeeds(client, action.pos); + if (action_type != ACTION_TYPE.NONE) { + queuedActions.add(new FarmerAction(action.pos, action_type)); + } + } + + if (queuedActions.size() >= options.max_actions_per_tick) + return; + } + } + + private static ACTION_TYPE holingSeeds(MinecraftClient client, BlockPos pos) { + if (client.world == null || client.player == null) + return ACTION_TYPE.NONE; + + ItemStack mainHandItem = client.player.getStackInHand(Hand.MAIN_HAND); + ItemStack offHandItem = client.player.getStackInHand(Hand.OFF_HAND); + + BlockState block = client.world.getBlockState(pos); + + for (int i = 0; i < crops.length; i++) { + AutoFarmerCropType cropType = crops[i]; + + if (cropType.seed_type != null && cropType.planted_on != null && block.isOf(cropType.planted_on) && + client.world.getBlockState(pos.up()).isAir() && options.seeds_enabled[i]) { + if (mainHandItem.isOf(cropType.seed_type)) { + return ACTION_TYPE.PLACE_MAIN_HAND; + } else if (offHandItem.isOf(cropType.seed_type)) { + return ACTION_TYPE.PLACE_OFF_HAND; + } + } + } + + return ACTION_TYPE.NONE; + } + + private static boolean isMature(BlockState block, Class blockType) { + if (block.getBlock() instanceof CropBlock) + return ((CropBlock) block.getBlock()).isMature(block); + if (blockType == NetherWartBlock.class) + return block.get(NetherWartBlock.AGE) >= NetherWartBlock.field_31199; + + return false; + } + + private static boolean isSecondBlock(ClientWorld world, BlockPos pos, Block crop_type) { + return world.getBlockState(pos).isOf(crop_type) && world.getBlockState(pos.down()).isOf(crop_type) && + !world.getBlockState(pos.down().down()).isOf(crop_type); + } + + private static void sendStopPackets(MinecraftClient client) { + if (client.world == null || client.player == null) + return; + + for (BlockPos pos: queuedStopPackets) { + PlayerActionC2SPacket packet = + new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, pos, Direction.UP, 0); + Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet); + } + + queuedStopPackets.clear(); + } + + private static void breakCrop(MinecraftClient client, BlockPos pos) { + if (client == null || client.player == null) + return; + + PlayerActionC2SPacket packet = + new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, pos, Direction.UP, 0); + Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet); + + queuedStopPackets.add(pos); + } + + private static void plantSeeds(MinecraftClient client, BlockPos pos, Hand hand) { + if (client == null || client.player == null) + return; + + PlayerInteractBlockC2SPacket packet = new PlayerInteractBlockC2SPacket(hand, + new BlockHitResult(new Vec3d(pos.getX(), pos.getY(), pos.getZ()), Direction.UP, pos, false), 0); + Objects.requireNonNull(client.getNetworkHandler()).sendPacket(packet); + } + + + public static class AutoFarmerCropType { + public Block crop_type; + public Item seed_type; + public GROWTH_PATTERN growth_pattern; + public Block planted_on; + + public AutoFarmerCropType(Block crop_type, Item seed_type, GROWTH_PATTERN growth_pattern, Block planted_on) { + this.crop_type = crop_type; + this.seed_type = seed_type; + this.growth_pattern = growth_pattern; + this.planted_on = planted_on; + } + } + + private record FarmerAction(BlockPos pos, ACTION_TYPE type) { } +} diff --git a/src/main/java/cmods/haxxor/client/HaxxorClient.java b/src/main/java/cmods/haxxor/client/HaxxorClient.java new file mode 100644 index 0000000..a38ca57 --- /dev/null +++ b/src/main/java/cmods/haxxor/client/HaxxorClient.java @@ -0,0 +1,53 @@ +package cmods.haxxor.client; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.StickyKeyBinding; +import org.lwjgl.glfw.GLFW; + +import java.util.Optional; + +@Environment(EnvType.CLIENT) +public class HaxxorClient implements ClientModInitializer { + public static String MOD_ID = "haxxor"; + public static String version = "Unknown"; + + private static StickyKeyBinding auto_farm_key; + private static final HaxxorOptions options = HaxxorOptions.getInstance(); + + @Override + public void onInitializeClient() { + Optional modContainer = FabricLoader.getInstance().getModContainer(MOD_ID); + modContainer.ifPresentOrElse(container -> version = container.getMetadata().getVersion().getFriendlyString(), + () -> System.out.println("Haxxor: Could not get mod version")); + + auto_farm_key = (StickyKeyBinding) KeyBindingHelper.registerKeyBinding(new StickyKeyBinding( + "key.haxxor.auto_farm", + GLFW.GLFW_KEY_Z, + "category.haxxor.options", + () -> true + )); + + AutoFarmer.init(); + + ClientTickEvents.END_CLIENT_TICK.register(this::tick); + } + + public static boolean toggleAutoFarmer() { + auto_farm_key.setPressed(true); + + return options.autoFarmer.enabled = auto_farm_key.isPressed(); + } + + private void tick(MinecraftClient client) { + options.autoFarmer.enabled = auto_farm_key.isPressed(); + + AutoFarmer.tick(client); + } +} diff --git a/src/main/java/cmods/haxxor/client/HaxxorOptions.java b/src/main/java/cmods/haxxor/client/HaxxorOptions.java new file mode 100644 index 0000000..f9997cb --- /dev/null +++ b/src/main/java/cmods/haxxor/client/HaxxorOptions.java @@ -0,0 +1,90 @@ +package cmods.haxxor.client; + +import java.util.prefs.Preferences; + +public final class HaxxorOptions { + private static HaxxorOptions instance = null; + + public final AutoFarmerOptions autoFarmer; + + + public static HaxxorOptions getInstance() { + if (instance == null) + instance = new HaxxorOptions(); + + return instance; + } + + private HaxxorOptions() { + autoFarmer = new AutoFarmerOptions(); + + load(); + } + + public void load() { + autoFarmer.load(); + } + + public void save() { + autoFarmer.save(); + } + + + + public static class AutoFarmerOptions { + private final Preferences prefs = Preferences.userNodeForPackage(AutoFarmerOptions.class); + + public boolean enabled; + public boolean move_seeds; + + public int min_y = -2; + public int max_y = 1; + + public int horizontal_reach = 4; + public int max_actions_per_tick = 30; + + public final boolean[] seeds_enabled = new boolean[AutoFarmer.crops.length]; + public final boolean[] crops_enabled = new boolean[AutoFarmer.crops.length]; + + public AutoFarmerOptions() { } + + + private void load() { + move_seeds = prefs.getBoolean("move_seeds", move_seeds); + min_y = prefs.getInt("min_y", min_y); + max_y = prefs.getInt("max_y", max_y); + horizontal_reach = prefs.getInt("horizontal_reach", horizontal_reach); + max_actions_per_tick = prefs.getInt("max_actions_per_tick", max_actions_per_tick); + + for (int i = 0; i < AutoFarmer.crops.length; i++) { + if (AutoFarmer.crops[i].seed_type != null) { + seeds_enabled[i] = prefs.getBoolean("seed_" + AutoFarmer.crops[i].seed_type.getName().getString(), + seeds_enabled[i]); + } + + if (AutoFarmer.crops[i].crop_type != null) { + crops_enabled[i] = prefs.getBoolean("crop_" + AutoFarmer.crops[i].crop_type.getName().getString(), + crops_enabled[i]); + } + } + } + + private void save() { + prefs.putBoolean("move_seeds", move_seeds); + prefs.putInt("min_y", min_y); + prefs.putInt("max_y", max_y); + prefs.putInt("horizontal_reach", horizontal_reach); + prefs.putInt("max_actions_per_tick", max_actions_per_tick); + + for (int i = 0; i < AutoFarmer.crops.length; i++) { + if (AutoFarmer.crops[i].seed_type != null) { + prefs.putBoolean("seed_" + AutoFarmer.crops[i].seed_type.getName().getString(), seeds_enabled[i]); + } + + if (AutoFarmer.crops[i].crop_type != null) { + prefs.putBoolean("crop_" + AutoFarmer.crops[i].crop_type.getName().getString(), crops_enabled[i]); + } + } + } + } +} diff --git a/src/main/java/cmods/haxxor/client/ModMenuConfig.java b/src/main/java/cmods/haxxor/client/ModMenuConfig.java new file mode 100644 index 0000000..1ee14a4 --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ModMenuConfig.java @@ -0,0 +1,13 @@ +package cmods.haxxor.client; + +import cmods.haxxor.client.ui.HaxxorOptionsScreen; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import net.minecraft.client.gui.screen.Screen; + +public class ModMenuConfig implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return (ConfigScreenFactory) HaxxorOptionsScreen::new; + } +} diff --git a/src/main/java/cmods/haxxor/client/ui/Constants.java b/src/main/java/cmods/haxxor/client/ui/Constants.java new file mode 100644 index 0000000..14cde60 --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/Constants.java @@ -0,0 +1,14 @@ +package cmods.haxxor.client.ui; + +public class Constants { + public static final int buttonWidth = 150; + public static final int buttonHeight = 20; + public static final int column1_offset = -5 - buttonWidth; + public static final int column2_offset = 5; + public static final float startHeight_multiplier = 1.0f / 6.0f; + public static final int rowIncrement = buttonHeight + 5; + + public static final int doneButtonWidth = buttonWidth + buttonWidth / 3; + public static final int doneButtonX_offset = -(doneButtonWidth / 2); + public static final int doneButtonRowIncrement = rowIncrement + 4; +} diff --git a/src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java b/src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java new file mode 100644 index 0000000..9702f18 --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java @@ -0,0 +1,80 @@ +package cmods.haxxor.client.ui; + +import cmods.haxxor.client.AutoFarmer; +import cmods.haxxor.client.HaxxorOptions; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; + +import static cmods.haxxor.client.ui.Constants.*; + +public class CropSelectScreen extends Screen { + private final HaxxorOptions options; + private final Screen parent; + private final int heightOffset = 10; + + + protected CropSelectScreen(Screen parent) { + super(Text.translatable("haxxor.options.farmer.crop_select.title")); + this.parent = parent; + options = HaxxorOptions.getInstance(); + } + + protected void init() { + if (client == null) + return; + + final int column1 = this.width / 2 + column1_offset; + final int column2 = this.width / 2 + column2_offset; + final int startHeight = (int) Math.floor(this.height * startHeight_multiplier) + heightOffset; + final int doneButtonX = this.width / 2 + doneButtonX_offset; + + int y = startHeight; + + for (int i = 0; i < AutoFarmer.crops.length; i++) { + int finalI = i; + + if (AutoFarmer.crops[i].seed_type != null) { + addDrawableChild(new ToggleEnableButtonWidget(column1, y, buttonWidth, buttonHeight, + AutoFarmer.crops[i].seed_type.getName(), options.autoFarmer.seeds_enabled[i], + (button, enable) -> options.autoFarmer.seeds_enabled[finalI] = enable)); + } + + if (AutoFarmer.crops[i].crop_type != null) { + addDrawableChild(new ToggleEnableButtonWidget(column2, y, buttonWidth, buttonHeight, + AutoFarmer.crops[i].crop_type.getName(), options.autoFarmer.crops_enabled[i], + (button, enable) -> options.autoFarmer.crops_enabled[finalI] = enable)); + } + + y += rowIncrement; + } + + addDrawableChild(new ButtonWidget(doneButtonX, y + doneButtonRowIncrement, doneButtonWidth, buttonHeight, + ScreenTexts.DONE, button -> { + options.save(); + client.setScreen(parent); + })); + } + + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + final int column1 = this.width / 2 + column1_offset; + final int column2 = this.width / 2 + column2_offset; + final int startHeight = (int) Math.floor(this.height * startHeight_multiplier) + heightOffset; + + this.renderBackground(matrices); + + drawCenteredText(matrices, this.textRenderer, Text.translatable("haxxor.options.farmer.crop_select.seeds"), + column1 + buttonWidth / 2, startHeight - rowIncrement, 0xffffff); + drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 0xffffff); + drawCenteredText(matrices, this.textRenderer, Text.translatable("haxxor.options.farmer.crop_select.blocks"), + column2 + buttonWidth / 2, startHeight - rowIncrement, 0xffffff); + + super.render(matrices, mouseX, mouseY, delta); + } + + public void removed() { + options.save(); + } +} diff --git a/src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java b/src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java new file mode 100644 index 0000000..d75451b --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java @@ -0,0 +1,41 @@ +package cmods.haxxor.client.ui; + +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.text.Text; + +public class CycleButtonWidget extends ButtonWidget { + private final Text[] options; + private final CycleAction onCycle; + private int selectedIndex; + + + public CycleButtonWidget(int x, int y, int width, int height, Text[] options, int startIndex, CycleAction onCycle) { + this(x, y, width, height, options, startIndex, onCycle, EMPTY); + } + + public CycleButtonWidget(int x, int y, int width, int height, Text[] options, int startIndex, CycleAction onCycle, TooltipSupplier tooltipSupplier) { + super(x, y, width, height, options[startIndex], onCycle, tooltipSupplier); + this.options = options; + this.onCycle = onCycle; + this.selectedIndex = startIndex; + } + + + @Override + public void onPress() { + selectedIndex++; + if (selectedIndex >= options.length) { + selectedIndex = 0; + } + + this.setMessage(options[selectedIndex]); + this.onCycle.onCycle(this, selectedIndex); + } + + public interface CycleAction extends PressAction { + void onCycle(ButtonWidget button, int newIndex); + + @Override + default void onPress(ButtonWidget button) { } + } +} diff --git a/src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java b/src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java new file mode 100644 index 0000000..76bb8bb --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java @@ -0,0 +1,115 @@ +package cmods.haxxor.client.ui; + +import cmods.haxxor.client.HaxxorClient; +import cmods.haxxor.client.HaxxorOptions; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +import static cmods.haxxor.client.ui.Constants.*; + +public class FarmerOptionsScreen extends Screen { + private final Screen parent; + private final HaxxorOptions options = HaxxorOptions.getInstance(); + + protected FarmerOptionsScreen(Screen parent) { + super(Text.translatable("haxxor.options.farmer.title")); + this.parent = parent; + } + + protected void init() { + if (client == null) + return; + + final Text enabledText = Text.translatable("haxxor.state.enabled"); + final Text disabledText = Text.translatable("haxxor.state.disabled"); + + final int column1 = this.width / 2 + column1_offset; + final int column2 = this.width / 2 + column2_offset; + final int startHeight = (int) Math.floor(this.height * startHeight_multiplier); + final int doneButtonX = this.width / 2 + doneButtonX_offset; + + int row = 0; + + addDrawableChild(new ButtonWidget(column1, startHeight, buttonWidth, buttonHeight, + options.autoFarmer.enabled ? enabledText : disabledText, + button -> button.setMessage(HaxxorClient.toggleAutoFarmer() ? enabledText : disabledText))); + + addDrawableChild(new ButtonWidget(column2, startHeight, buttonWidth, buttonHeight, + Text.translatable("haxxor.options.farmer.edit_actions"), + button -> client.setScreen(new CropSelectScreen(this)))); + + row++; + + addDrawableChild(new ToggleEnableButtonWidget(column1, startHeight + (rowIncrement * row), buttonWidth, + buttonHeight, Text.literal("Move Seeds"), options.autoFarmer.move_seeds, + (button, isEnabled) -> options.autoFarmer.move_seeds = isEnabled)); + + row++; + + addVariableAdjust(column1, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.min_y, + Text.translatable("haxxor.options.farmer.min_y"), + () -> --options.autoFarmer.min_y, + () -> ++options.autoFarmer.min_y); + + addVariableAdjust(column2, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.max_y, + Text.translatable("haxxor.options.farmer.max_y"), + () -> --options.autoFarmer.max_y, + () -> ++options.autoFarmer.max_y); + + row++; + + addVariableAdjust(column1, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.horizontal_reach, + Text.translatable("haxxor.options.farmer.reach"), + () -> --options.autoFarmer.horizontal_reach, + () -> ++options.autoFarmer.horizontal_reach); + + addVariableAdjust(column2, startHeight + (rowIncrement * row), buttonWidth, buttonHeight, options.autoFarmer.max_actions_per_tick, + Text.translatable("haxxor.options.farmer.actions_per_tick"), + () -> options.autoFarmer.max_actions_per_tick -= 5, + () -> options.autoFarmer.max_actions_per_tick += 5); + + row++; + + this.addDrawableChild(new ButtonWidget(doneButtonX, + startHeight + (rowIncrement * row) + doneButtonRowIncrement, doneButtonWidth, buttonHeight, + ScreenTexts.DONE, button -> client.setScreen(parent))); + } + + @SuppressWarnings("SameParameterValue") + private void addVariableAdjust(int x, int y, int width, int height, int initialValue, @NotNull Text label, + UpdateTextAction minus, UpdateTextAction add) { + final int button_width = height + height / 4; + final String labelStr = label.getString() + ": "; + + ButtonWidget middle = new ButtonWidget(x + button_width - 1, y, width - (button_width * 2) + 2, height, + Text.literal(labelStr + initialValue), button -> {}); + this.addDrawableChild(middle); + + this.addDrawableChild(new ButtonWidget(x, y, button_width, height, Text.literal("-"), + button -> middle.setMessage(Text.literal(labelStr + minus.onUpdate())))); + this.addDrawableChild(new ButtonWidget(x + width - button_width + 1, y, button_width, height, Text.literal("+"), + button -> middle.setMessage(Text.literal(labelStr + add.onUpdate())))); + } + + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); + drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 15, 0xffffff); + super.render(matrices, mouseX, mouseY, delta); + } + + public void removed() { + options.save(); + } + + + @Environment(EnvType.CLIENT) + private interface UpdateTextAction { + int onUpdate(); + } +} diff --git a/src/main/java/cmods/haxxor/client/ui/HaxxorOptionsScreen.java b/src/main/java/cmods/haxxor/client/ui/HaxxorOptionsScreen.java new file mode 100644 index 0000000..77d765e --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/HaxxorOptionsScreen.java @@ -0,0 +1,46 @@ +package cmods.haxxor.client.ui; + +import cmods.haxxor.client.HaxxorClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; + +import static cmods.haxxor.client.ui.Constants.*; + +public class HaxxorOptionsScreen extends Screen { + private final Screen parent; + + public HaxxorOptionsScreen(Screen parent) { + super(Text.translatable("haxxor.options.title")); + this.parent = parent; + } + + protected void init() { + if (client == null) + return; + + final int column1 = this.width / 2 + column1_offset; + final int startHeight = (int) Math.floor(this.height * startHeight_multiplier); + final int doneButtonX = this.width / 2 + doneButtonX_offset; + + this.addDrawableChild(new ButtonWidget(column1, startHeight, buttonWidth, buttonHeight, + Text.translatable("haxxor.options.farmer"), + button -> this.client.setScreen(new FarmerOptionsScreen(this)))); + + this.addDrawableChild(new ButtonWidget(doneButtonX, startHeight + rowIncrement + doneButtonRowIncrement, + doneButtonWidth, buttonHeight, ScreenTexts.DONE, button -> this.client.setScreen(this.parent))); + } + + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + renderBackground(matrices); + Text versionText = Text.literal("v" + HaxxorClient.version); + + + drawCenteredText(matrices, textRenderer, title, this.width / 2, 15, 0xffffff); + drawTextWithShadow(matrices, textRenderer, versionText, this.width - textRenderer.getWidth(versionText) - 2, + this.height - textRenderer.fontHeight - 2, 0xffffff); + super.render(matrices, mouseX, mouseY, delta); + } +} diff --git a/src/main/java/cmods/haxxor/client/ui/ToggleEnableButtonWidget.java b/src/main/java/cmods/haxxor/client/ui/ToggleEnableButtonWidget.java new file mode 100644 index 0000000..fa223cc --- /dev/null +++ b/src/main/java/cmods/haxxor/client/ui/ToggleEnableButtonWidget.java @@ -0,0 +1,51 @@ +package cmods.haxxor.client.ui; + +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; + +public class ToggleEnableButtonWidget extends ButtonWidget { + private final String name; + private boolean enabled; + private final ToggleAction onToggle; + + private final String enabledText = Text.translatable("haxxor.state.enabled").getString(); + private final String disabledText = Text.translatable("haxxor.state.disabled").getString(); + + + public ToggleEnableButtonWidget(int x, int y, int width, int height, Text name, boolean startEnabled, + ToggleAction onToggle) { + this(x, y, width, height, name, startEnabled, onToggle, EMPTY); + } + + public ToggleEnableButtonWidget(int x, int y, int width, int height, Text name, boolean startEnabled, + ToggleAction onToggle, TooltipSupplier tooltipSupplier) { + super(x, y, width, height, name, onToggle, tooltipSupplier); + this.name = name.getString() + ": "; + this.enabled = startEnabled; + this.onToggle = onToggle; + } + + private void updateText() { + setMessage(Text.literal(name + (enabled ? enabledText : disabledText))); + } + + + @Override + public void onPress() { + onToggle.onToggle(this, enabled = !enabled); + } + + @Override + public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) { + updateText(); + super.renderButton(matrices, mouseX, mouseY, delta); + } + + public interface ToggleAction extends PressAction { + void onToggle(ButtonWidget button, boolean isEnabled); + + @Override + default void onPress(ButtonWidget button) { } + } +} diff --git a/src/main/java/cmods/haxxor/mixin/HudMixin.java b/src/main/java/cmods/haxxor/mixin/HudMixin.java new file mode 100644 index 0000000..daf7d61 --- /dev/null +++ b/src/main/java/cmods/haxxor/mixin/HudMixin.java @@ -0,0 +1,60 @@ +package cmods.haxxor.mixin; + +import cmods.haxxor.client.AutoFarmer; +import cmods.haxxor.client.HaxxorOptions; +import com.mojang.datafixers.util.Pair; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; + +@Mixin(InGameHud.class) +public abstract class HudMixin extends DrawableHelper { + + @Shadow @Final private MinecraftClient client; + @Shadow public abstract TextRenderer getTextRenderer(); + + + @Inject(at = @At("TAIL"), method = "render") + private void render(MatrixStack matrices, float tickDelta, CallbackInfo callback) { + if (this.client.options.debugEnabled || client.player == null) + return; + + HaxxorOptions options = HaxxorOptions.getInstance(); + TextRenderer textRenderer = this.getTextRenderer(); + ArrayList> lines = new ArrayList<>(); + + int x = 5; + int y = 5; + + int red = 0xff0000; + int green = 0x00ff00; + + + if (options.autoFarmer.enabled) { + lines.add(new Pair<>(Text.translatable("haxxor.state.on"), green)); + } else { + lines.add(new Pair<>(Text.translatable("haxxor.state.off"), red)); + } + + if (AutoFarmer.canFarm()) { + lines.add(new Pair<>(Text.translatable("haxxor.actions.available"), green)); + } + + + for (Pair line: lines) { + DrawableHelper.drawTextWithShadow(matrices, textRenderer, line.getFirst(), x, y, line.getSecond()); + y += 10; + } + } +} \ No newline at end of file diff --git a/src/main/java/cmods/haxxor/mixin/OptionsMixin.java b/src/main/java/cmods/haxxor/mixin/OptionsMixin.java new file mode 100644 index 0000000..a563f8a --- /dev/null +++ b/src/main/java/cmods/haxxor/mixin/OptionsMixin.java @@ -0,0 +1,29 @@ +package cmods.haxxor.mixin; + +import cmods.haxxor.client.ui.HaxxorOptionsScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.option.OptionsScreen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(OptionsScreen.class) +public class OptionsMixin extends Screen { + + protected OptionsMixin(Text title) { + super(title); + } + + @Inject(at = @At("TAIL"), method = "init") + private void init(CallbackInfo ci) { + if (client == null) + return; + + this.addDrawableChild(new ButtonWidget(20, 20, 100, 20, + Text.translatable("haxxor.options"), (button) -> + client.setScreen(new HaxxorOptionsScreen(this)))); + } +} diff --git a/src/main/java/cmods/haxxor/mixin/PauseMixin.java b/src/main/java/cmods/haxxor/mixin/PauseMixin.java new file mode 100644 index 0000000..0831c2e --- /dev/null +++ b/src/main/java/cmods/haxxor/mixin/PauseMixin.java @@ -0,0 +1,30 @@ +package cmods.haxxor.mixin; + + +import cmods.haxxor.client.ui.HaxxorOptionsScreen; +import net.minecraft.client.gui.screen.GameMenuScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GameMenuScreen.class) +public class PauseMixin extends Screen { + + protected PauseMixin(Text title) { + super(title); + } + + @Inject(at = @At("TAIL"), method = "init") + public void init(CallbackInfo ci) { + if (client == null) + return; + + this.addDrawableChild(new ButtonWidget(20, 20, 100, 20, + Text.translatable("haxxor.options"), (button) -> + client.setScreen(new HaxxorOptionsScreen(this)))); + } +} diff --git a/src/main/resources/assets/haxxor/icon.png b/src/main/resources/assets/haxxor/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cabae41d953fc8c646e3ec6c69e2115e2c0b0632 GIT binary patch literal 1215 zcmZXPdrVVz6vt10lzJ(2UW5XX0VQ>U-~*&()hWFQofQQOLUAIt_yh#T6xqNIZwrVz z4P*!c;)88;IH*+d8L(7HM1>B9GdO7@P~Sr-HqfC^R{vYFlbrL-$v2B7I0|=+~FywcAJF?AYlG9IJ5Onf4Ne+7WaEX<4c9 zzW69oFYYe2@ud%!zokU`J<>!^76eV!&xwM5 zZqYV55QC?@S5brmc)mS?Oi8|p%1VxS$;9Bw_pffdkU5OHL>BbffQ|nFWDh|0!OD)h zE#dQjy2EzLqLG_hB6JZY9URzG{kG7~nN|)bL#-jWkX41wUmHKdssiHlxR9l_J8zhF zciJ+DIN<+$?_rn+93%HSl?Q#g0Wsq|sJCktVp34=*0^CTP^W8X*q?S%!}MbuC;OVi z{7M@q&JtWujaq9Gh$Wz^mN=pcxRPGLz%cE-TgjJY_srx<*z#niz7Z&88GoStQ3;d; z$hORG6gyxTAvY|(CD>&9TZ314A}*5z9MbAJtDBF~wTAXyQ#Ia@N}-ZawbXdw-X-)) z02X!*NYASInlyMc+iBuCDqfcmh=2~)RGDiaP40mx0Ku+AtJLBDMhj#tH5~|=^?~%yD?gcRGPFO7orz`zYJ+2_Lz~$T*ch31u(Uj6 zE)0@BiV$7mei{p?WU0cNR&xQGki>yEC`(vkOHcCYCe@Gxj~XpFU~4noh;)d+?!PIh z_}Gn9_M-Zz*~Y&b{_%KvkkS8GuD&vro}~Q(R}rF48OomaQa-|vntgR5K-4WeLN|?c z1W>uU5~Lk5revCjK~0s@%)S&g$lc5-6k@e|>T>T9f#`0C$Ngc^&r>EQ)Xz)hK5>FA z;e8ox)ow%SE>{PUVq8gSNN_GY{xu@!`BdQ1hXw=_FNVr~B~J!lQXxaeDRH!287i%~ zN!1=J?^uPAB3|GFjAZ&1=iyM7Emk^1U<}>Nf;GcU8`vPOb$uQVMIB#Ne+f;zMLmUp z6}K!u0h_;xt6Ccb-IrHg9B4F!)+%G=2a;PgW6RF%`0%bIQ#(^|=(w>VcwozX>3l? zeJm$tt>NabrdMO@y{h2=GRI!@}3DO77g>(uSFlyX<)|0tgdJ!<{1m4^0EFUBP8 Rz6`cmCy0r>xJIgge*uIs18)ET literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/haxxor/lang/en_us.json b/src/main/resources/assets/haxxor/lang/en_us.json new file mode 100644 index 0000000..dd181ce --- /dev/null +++ b/src/main/resources/assets/haxxor/lang/en_us.json @@ -0,0 +1,26 @@ +{ + "haxxor.state.on": "On", + "haxxor.state.off": "Off", + "haxxor.state.enabled": "Enabled", + "haxxor.state.disabled": "Disabled", + + "haxxor.actions.available": "Actions available", + "haxxor.actions.unavailable": "Actions unavailable", + + "haxxor.options": "Haxxor", + "haxxor.options.title": "Haxxor Options", + "haxxor.options.farmer": "Auto Farm", + "haxxor.options.farmer.title": "Auto Farm Options", + "haxxor.options.farmer.max_y": "Max Y Offset", + "haxxor.options.farmer.min_y": "Min Y Offset", + "haxxor.options.farmer.reach": "Reach", + "haxxor.options.farmer.edit_actions": "Configure Actions", + "haxxor.options.farmer.actions_per_tick": "Max Per Tick", + "haxxor.options.farmer.crop_select.title": "Toggle Actions", + "haxxor.options.farmer.crop_select.seeds": "Seeds to Plant", + "haxxor.options.farmer.crop_select.blocks": "Crops to Break", + + "category.haxxor.options": "Haxxor", + "key.haxxor.auto_farm": "Auto Farm Toggle", + "key.haxxor.swap_items": "Swap Items" +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..c8d7a3b --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,30 @@ +{ + "schemaVersion": 1, + "id": "haxxor", + "version": "${version}", + "name": "Haxxor", + "description": "A collection of hacks because yes", + "authors": [ + "Cameron Reed" + ], + "contact": { + "sources": "https://gitea.cam123.dev/CameronReed/cmods-haxxor" + }, + "license": "MIT", + "icon": "assets/haxxor/icon.png", + "environment": "client", + "entrypoints": { + "client": [ + "cmods.haxxor.client.HaxxorClient" + ], + "modmenu": ["cmods.haxxor.client.ModMenuConfig"] + }, + "mixins": [ + "haxxor.mixins.json" + ], + "depends": { + "fabricloader": ">=0.14.10", + "fabric": "*", + "minecraft": "1.19.2" + } +} diff --git a/src/main/resources/haxxor.mixins.json b/src/main/resources/haxxor.mixins.json new file mode 100644 index 0000000..fe88e78 --- /dev/null +++ b/src/main/resources/haxxor.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "cmods.haxxor.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "HudMixin", + "OptionsMixin", + "PauseMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}