diff --git a/gradle.properties b/gradle.properties index 2dfa55a0..e32f8339 100644 --- a/gradle.properties +++ b/gradle.properties @@ -254,7 +254,7 @@ enable_xu2 = false # If this is set to false, the mixin will not apply. enable_rocketry = false -# Whether to enable Architecture Craft in runtime. Enables the mixin which adds more Architecture Craft Slopes, improves the Sawbench UI, and fixes the Sawbench Particle Texture. +# Whether to enable Architecture Craft in runtime. Enables the mixin which adds more Architecture Craft Slopes, improves the Sawbench UI, fixes the Sawbench Particle Texture, and fixes Shapes' Harvest Tools and Levels in The One Probe. # If this is set to false, the mixin will not apply. enable_architecture_craft = false diff --git a/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java b/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java index 0bd7190b..127f2976 100644 --- a/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java +++ b/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java @@ -206,7 +206,7 @@ public static class ModIntegration { public boolean enableAdvancedRocketryIntegration = true; @Config.Comment({ - "Whether to enable ArchitectureCraft Integration, which adds new slope variants, improves the GUI of the Sawbench, and fixes the Sawbench Particle Texture.", + "Whether to enable ArchitectureCraft Integration, which adds new slope variants, improves the GUI of the Sawbench, fixes the Sawbench Particle Texture, and fixes Shapes' Harvest Tools and Levels in The One Probe.", "[default: true]" }) @Config.LangKey("config.nomilabs.mod_integration.architecture_craft") @Config.RequiresMcRestart diff --git a/src/main/java/com/nomiceu/nomilabs/core/LabsLateMixin.java b/src/main/java/com/nomiceu/nomilabs/core/LabsLateMixin.java index 1b7b4a5a..a1246f42 100644 --- a/src/main/java/com/nomiceu/nomilabs/core/LabsLateMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/core/LabsLateMixin.java @@ -42,7 +42,8 @@ public class LabsLateMixin implements ILateMixinLoader { new AbstractMap.SimpleImmutableEntry<>(LabsValues.FTB_UTILS_MODID, LabsConfig.modIntegration.enableFTBUtilsIntegration), new AbstractMap.SimpleImmutableEntry<>(LabsValues.TOP_ADDONS_MODID, - LabsConfig.modIntegration.enableTopAddonsIntegration)) + LabsConfig.modIntegration.enableTopAddonsIntegration), + new AbstractMap.SimpleImmutableEntry<>(LabsValues.TOP_MODID, true)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @Override diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/architecturecraft/BlockShapeMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/architecturecraft/BlockShapeMixin.java new file mode 100644 index 00000000..bd2dea49 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/architecturecraft/BlockShapeMixin.java @@ -0,0 +1,65 @@ +package com.nomiceu.nomilabs.mixin.architecturecraft; + +import com.elytradev.architecture.common.block.BlockArchitecture; +import com.elytradev.architecture.common.block.BlockShape; +import com.elytradev.architecture.common.tile.TileShape; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +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.CallbackInfoReturnable; + +/** + * Improves Determining Whether the Shape can be Harvested, and the Hardness of the Shape. + */ +@Mixin(value = BlockShape.class, remap = false) +public class BlockShapeMixin extends BlockArchitecture { + /** + * Default Ignored Constructor + */ + public BlockShapeMixin(Material material) { + super(material); + } + + @Inject(method = "acBlockStrength", at = @At(value = "INVOKE", target = "Lcom/elytradev/architecture/common/block/BlockShape;acCanHarvestBlock(Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/entity/player/EntityPlayer;)Z"), cancellable = true) + private static void baseBlockStrength(IBlockState state, EntityPlayer player, World world, BlockPos pos, CallbackInfoReturnable cir, @Local(ordinal = 1) float strength) { + cir.setReturnValue(state.getBlock().canHarvestBlock(world, pos, player) ? strength / 100.0F : strength / 30.0F); + } + + /** + * Modified from {@link net.minecraftforge.common.ForgeHooks#canHarvestBlock(Block, EntityPlayer, IBlockAccess, BlockPos)} + */ + @SuppressWarnings("DuplicatedCode") + @Override + public boolean canHarvestBlock(@NotNull IBlockAccess world, @NotNull BlockPos pos, @NotNull EntityPlayer player) { + var te = TileShape.get(world, pos); + if (te == null) return super.canHarvestBlock(world, pos, player); + + var baseState = te.getBaseBlockState(); + var baseBlock = baseState.getBlock(); + + if (baseState.getMaterial().isToolNotRequired()) { + return true; + } + + ItemStack stack = player.getHeldItemMainhand(); + String tool = baseBlock.getHarvestTool(baseState); + if (stack.isEmpty() || tool == null) + return player.canHarvestBlock(baseState); + + int toolLevel = stack.getItem().getHarvestLevel(stack, tool, player, baseState); + if (toolLevel < 0) + return player.canHarvestBlock(baseState); + + return toolLevel >= baseBlock.getHarvestLevel(baseState); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/theoneprobe/HarvestInfoToolsMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/theoneprobe/HarvestInfoToolsMixin.java new file mode 100644 index 00000000..48f1313a --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/theoneprobe/HarvestInfoToolsMixin.java @@ -0,0 +1,136 @@ +package com.nomiceu.nomilabs.mixin.theoneprobe; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemTool; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.fml.common.Loader; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalIntRef; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import com.nomiceu.nomilabs.LabsValues; +import com.nomiceu.nomilabs.config.LabsConfig; + +import mcjty.theoneprobe.api.IProbeInfo; +import mcjty.theoneprobe.apiimpl.providers.HarvestInfoTools; + +/** + * Fixes Architecture Craft Shapes' Displays on TOP. + * As far as I can see, no API to change this outside of mixins. + */ +@Mixin(value = HarvestInfoTools.class, remap = false) +public class HarvestInfoToolsMixin { + + @Shadow + @Final + private static HashMap testTools; + + /** + * Only change the main `showHarvestInfo` method. + * This is called using the normal configs. The other two methods are only called on some specific configurations. + *

+ * `showCanBeHarvested` displays correct results. + * `showHarvestLevel` does not provide world or position. + */ + @Inject(method = "showHarvestInfo", + at = @At(value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/block/Block;getHarvestTool(Lnet/minecraft/block/state/IBlockState;)Ljava/lang/String;")) + private static void getHarvestToolForArchitectureCraft(IProbeInfo probeInfo, World world, BlockPos pos, Block block, + IBlockState blockState, EntityPlayer player, CallbackInfo ci, + @Local LocalRef harvestTool) { + if (!Loader.isModLoaded(LabsValues.ARCHITECTURE_MODID) || + !LabsConfig.modIntegration.enableArchitectureCraftIntegration || isNotShapeForArchitectureCraft(block)) + return; + + var baseBlockState = getBaseBlockStateForArchitectureCraft(world, pos); + if (baseBlockState == null) return; + + var retrievedHarvestTool = baseBlockState.getBlock().getHarvestTool(baseBlockState); + + // From the Original Function + if (retrievedHarvestTool == null) { + // The block doesn't have an explicitly-set harvest tool, so test our wooden tools against it + float blockHardness = baseBlockState.getBlockHardness(world, pos); + if (blockHardness > 0f) { + for (Map.Entry testToolEntry : testTools.entrySet()) { + // loop through our test tools until we find a winner. + ItemStack testTool = testToolEntry.getValue(); + + if (testTool != null && testTool.getItem() instanceof ItemTool toolItem) { + if (testTool.getDestroySpeed(baseBlockState) >= toolItem.toolMaterial.getEfficiency()) { + retrievedHarvestTool = testToolEntry.getKey(); + break; + } + } + } + } + } + harvestTool.set(retrievedHarvestTool); + } + + @Inject(method = "showHarvestInfo", + at = @At(value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/block/Block;getHarvestLevel(Lnet/minecraft/block/state/IBlockState;)I")) + private static void getHarvestLevelForArchitectureCraft(IProbeInfo probeInfo, World world, BlockPos pos, + Block block, IBlockState blockState, EntityPlayer player, + CallbackInfo ci, @Local LocalIntRef harvestLevel) { + if (!Loader.isModLoaded(LabsValues.ARCHITECTURE_MODID) || + !LabsConfig.modIntegration.enableArchitectureCraftIntegration || isNotShapeForArchitectureCraft(block)) + return; + + var baseBlockState = getBaseBlockStateForArchitectureCraft(world, pos); + if (baseBlockState == null) return; + harvestLevel.set(baseBlockState.getBlock().getHarvestLevel(baseBlockState)); + } + + /** + * Use Reflection to prevent hard dep. Equivalent to `!(block instanceof BlockShape)`. + */ + @Unique + private static boolean isNotShapeForArchitectureCraft(Block block) { + try { + var shapeClass = Class.forName("com.elytradev.architecture.common.block.BlockShape"); + return !shapeClass.isInstance(block); + } catch (ClassNotFoundException e) { + return true; + } + } + + /** + * Use Reflection to prevent hard dep. Equivalent to `TileShape.get(world, pos).getBaseBlockState()`. + */ + @Unique + @Nullable + private static IBlockState getBaseBlockStateForArchitectureCraft(World world, BlockPos pos) { + try { + var tileShapeClass = Class.forName("com.elytradev.architecture.common.tile.TileShape"); + var getTileMethod = tileShapeClass.getDeclaredMethod("get", IBlockAccess.class, BlockPos.class); + var tile = getTileMethod.invoke(null, world, pos); + if (tile == null) return null; + + var getBaseBlockStateMethod = tile.getClass().getDeclaredMethod("getBaseBlockState"); + return (IBlockState) getBaseBlockStateMethod.invoke(tile); + } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { + return null; + } + } +} diff --git a/src/main/resources/mixins.nomilabs.architecturecraft.json b/src/main/resources/mixins.nomilabs.architecturecraft.json index 97c5bca6..b4c61ced 100644 --- a/src/main/resources/mixins.nomilabs.architecturecraft.json +++ b/src/main/resources/mixins.nomilabs.architecturecraft.json @@ -6,6 +6,7 @@ "compatibilityLevel": "JAVA_8", "mixins": [ "BlockSawbenchMixin", + "BlockShapeMixin", "ShapeAccessor", "TileSawbenchMixin" ], diff --git a/src/main/resources/mixins.nomilabs.theoneprobe.json b/src/main/resources/mixins.nomilabs.theoneprobe.json new file mode 100644 index 00000000..1d7e6671 --- /dev/null +++ b/src/main/resources/mixins.nomilabs.theoneprobe.json @@ -0,0 +1,12 @@ +{ + "package": "com.nomiceu.nomilabs.mixin.theoneprobe", + "refmap": "mixins.nomilabs.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "HarvestInfoToolsMixin" + ], + "client": [], + "server": [] +}