Initial Commit
This commit is contained in:
parent
8749a28cdc
commit
1f2c68181e
119
.gitignore
vendored
Normal file
119
.gitignore
vendored
Normal file
@ -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
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -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.
|
86
build.gradle
Normal file
86
build.gradle
Normal file
@ -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.
|
||||||
|
}
|
||||||
|
}
|
14
gradle.properties
Normal file
14
gradle.properties
Normal file
@ -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
|
1
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
1
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
9
settings.gradle
Normal file
9
settings.gradle
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = 'Fabric'
|
||||||
|
url = 'https://maven.fabricmc.net/'
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
322
src/main/java/cmods/haxxor/client/AutoFarmer.java
Normal file
322
src/main/java/cmods/haxxor/client/AutoFarmer.java
Normal file
@ -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<FarmerAction> potentialActions = new ArrayList<>();
|
||||||
|
private static final ArrayList<FarmerAction> queuedActions = new ArrayList<>();
|
||||||
|
private static final ArrayList<BlockPos> 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<ItemStack> 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<ItemStack> 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) { }
|
||||||
|
}
|
53
src/main/java/cmods/haxxor/client/HaxxorClient.java
Normal file
53
src/main/java/cmods/haxxor/client/HaxxorClient.java
Normal file
@ -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> 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);
|
||||||
|
}
|
||||||
|
}
|
90
src/main/java/cmods/haxxor/client/HaxxorOptions.java
Normal file
90
src/main/java/cmods/haxxor/client/HaxxorOptions.java
Normal file
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/cmods/haxxor/client/ModMenuConfig.java
Normal file
13
src/main/java/cmods/haxxor/client/ModMenuConfig.java
Normal file
@ -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<Screen>) HaxxorOptionsScreen::new;
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/cmods/haxxor/client/ui/Constants.java
Normal file
14
src/main/java/cmods/haxxor/client/ui/Constants.java
Normal file
@ -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;
|
||||||
|
}
|
80
src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java
Normal file
80
src/main/java/cmods/haxxor/client/ui/CropSelectScreen.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
41
src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java
Normal file
41
src/main/java/cmods/haxxor/client/ui/CycleButtonWidget.java
Normal file
@ -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) { }
|
||||||
|
}
|
||||||
|
}
|
115
src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java
Normal file
115
src/main/java/cmods/haxxor/client/ui/FarmerOptionsScreen.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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) { }
|
||||||
|
}
|
||||||
|
}
|
60
src/main/java/cmods/haxxor/mixin/HudMixin.java
Normal file
60
src/main/java/cmods/haxxor/mixin/HudMixin.java
Normal file
@ -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<Pair<Text, Integer>> 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<Text, Integer> line: lines) {
|
||||||
|
DrawableHelper.drawTextWithShadow(matrices, textRenderer, line.getFirst(), x, y, line.getSecond());
|
||||||
|
y += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/cmods/haxxor/mixin/OptionsMixin.java
Normal file
29
src/main/java/cmods/haxxor/mixin/OptionsMixin.java
Normal file
@ -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))));
|
||||||
|
}
|
||||||
|
}
|
30
src/main/java/cmods/haxxor/mixin/PauseMixin.java
Normal file
30
src/main/java/cmods/haxxor/mixin/PauseMixin.java
Normal file
@ -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))));
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/resources/assets/haxxor/icon.png
Normal file
BIN
src/main/resources/assets/haxxor/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
26
src/main/resources/assets/haxxor/lang/en_us.json
Normal file
26
src/main/resources/assets/haxxor/lang/en_us.json
Normal file
@ -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"
|
||||||
|
}
|
30
src/main/resources/fabric.mod.json
Normal file
30
src/main/resources/fabric.mod.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
16
src/main/resources/haxxor.mixins.json
Normal file
16
src/main/resources/haxxor.mixins.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"required": true,
|
||||||
|
"minVersion": "0.8",
|
||||||
|
"package": "cmods.haxxor.mixin",
|
||||||
|
"compatibilityLevel": "JAVA_17",
|
||||||
|
"mixins": [
|
||||||
|
],
|
||||||
|
"client": [
|
||||||
|
"HudMixin",
|
||||||
|
"OptionsMixin",
|
||||||
|
"PauseMixin"
|
||||||
|
],
|
||||||
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user