diff --git a/CHANGELOG.md b/CHANGELOG.md index e12f5f1..e06429b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v1.3.1 +## Tinkers' Construct Integration +- Fixed TiC main-hand + GT off-hand combination not using the GT off-hand tool when it is the appropriate tool for the block +- Fixed duplicate durability loss on both GT and TiC cross-hand combinations + +* * * + # v1.3.0 ## New: Storage Drawers Integration - GT materials can now be used to craft _Storage Upgrades_. diff --git a/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java b/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java index 6099117..f274cec 100644 --- a/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java +++ b/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java @@ -2,9 +2,7 @@ import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; -import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Enchantments; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraftforge.common.ForgeHooks; @@ -18,13 +16,9 @@ import slimeknights.tconstruct.library.utils.ToolHelper; /** - * Extends TiC's off-hand mining to cover GT + TiC cross-hand combinations. - * - *
* Registered at {@link EventPriority#LOW} so TiC's own {@code BreakSpeed} handler fires first. */ public final class DualToolHandler { @@ -53,27 +47,8 @@ public static void onBreakSpeed(net.minecraftforge.event.entity.player.PlayerEve event.setNewSpeed(speed); } } - - if (mainhand.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool) { - if (!ToolHelper.canHarvest(mainhand, state) && canHarvestForDrops(player, offhand, state)) { - float speed = offhand.getItem().getDestroySpeed(offhand, state); - if (speed > 1.0f) { - int efficiency = EnchantmentHelper.getEnchantmentLevel(Enchantments.EFFICIENCY, offhand); - if (efficiency > 0) speed += efficiency * efficiency + 1; - } - event.setNewSpeed(speed); - } - } } - /** - * Damages the off-hand tool after a block is broken via off-hand mining. - * Drops are handled by vanilla: {@code MixinEntityPlayer} makes - * {@code player.canHarvestBlock()} return {@code true} for GT+TiC combos, - * so {@code tryHarvestBlock} calls {@code harvestBlock} exactly once. - * - * Registered at {@link EventPriority#LOWEST} so other mods' cancellations are resolved first. - */ @SubscribeEvent(priority = EventPriority.LOWEST) public static void onBlockBreak(BlockEvent.BreakEvent event) { if (event.isCanceled()) return; @@ -90,16 +65,6 @@ public static void onBlockBreak(BlockEvent.BreakEvent event) { if (mainhand.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) { if (!canHarvestForDrops(player, mainhand, state) && ToolHelper.canHarvest(offhand, state)) { - // Drops are provided by vanilla: MixinEntityPlayer makes player.canHarvestBlock() - // return true for this combo, so tryHarvestBlock's harvestBlock() call fires normally. - // Only damage the TiC off-hand tool for its contribution to the harvest. - offhand.getItem().onBlockDestroyed(offhand, player.world, state, - event.getPos(), player); - } - } - - if (mainhand.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool) { - if (!ToolHelper.canHarvest(mainhand, state) && canHarvestForDrops(player, offhand, state)) { offhand.getItem().onBlockDestroyed(offhand, player.world, state, event.getPos(), player); } @@ -113,9 +78,6 @@ private static boolean canHarvestForDrops(EntityPlayer player, ItemStack stack, if (stack.isEmpty() || toolType == null) { return player.canHarvestBlock(state); } - // Null player: bypasses MixinItemGTTool's off-hand elevation so the raw level of - // this specific tool is returned. The fallback only fires when the tool itself - // cannot harvest. int level = stack.getItem().getHarvestLevel(stack, toolType, null, state); if (level < 0) return player.canHarvestBlock(state); return level >= block.getHarvestLevel(state); diff --git a/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCClientEvents.java b/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCClientEvents.java new file mode 100644 index 0000000..fb45d0b --- /dev/null +++ b/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCClientEvents.java @@ -0,0 +1,38 @@ +package com.github.gtexpert.gtmt.integration.tic; + +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import com.github.gtexpert.gtmt.integration.tic.materials.ToolMaterialRegistrar; + +/** + * Client-side event handlers for the TiC integration. + * + *
+ * Registered only on the client via {@link TiCModule#getEventBusSubscribers()}. + */ +@SideOnly(Side.CLIENT) +public class TiCClientEvents { + + @SubscribeEvent + public static void onRegisterModels(ModelRegistryEvent event) { + ToolMaterialRegistrar.registerFluidModels(); + } + + /** + * Re-inject dynamic material name translations after every resource reload. + * + *
+ * {@link net.minecraft.util.text.translation.LanguageMap#inject} entries are wiped
+ * whenever the language manager reloads (F3+T or in-game language change).
+ * {@link TextureStitchEvent.Post} fires after the language map has already been
+ * refreshed from disk, so this is the correct point to restore the dynamic entries.
+ */
+ @SubscribeEvent
+ public static void onTextureStitchPost(TextureStitchEvent.Post event) {
+ ToolMaterialRegistrar.reinjectTranslations();
+ }
+}
diff --git a/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCModule.java b/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCModule.java
index 317b52c..196a11a 100644
--- a/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCModule.java
+++ b/src/main/java/com/github/gtexpert/gtmt/integration/tic/TiCModule.java
@@ -1,14 +1,13 @@
package com.github.gtexpert.gtmt.integration.tic;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.minecraft.block.Block;
-import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.event.RegistryEvent;
-import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
import net.minecraftforge.fml.relauncher.Side;
-import net.minecraftforge.fml.relauncher.SideOnly;
import org.jetbrains.annotations.NotNull;
@@ -31,7 +30,11 @@ public class TiCModule extends IntegrationSubmodule {
@NotNull
@Override
public List
+ * Must be called after every resource reload (e.g. F3+T or language change) because
+ * {@link LanguageMap#inject} entries are cleared when the language manager reloads.
+ * Calling {@link Material#getLocalizedName()} here (rather than caching the string)
+ * ensures the correct translation for the currently active language is used.
+ */
+ public static void reinjectTranslations() {
+ translationSources.forEach((key, mat) -> doInject(key, mat.getLocalizedName()));
+ }
+
+ private static void doInject(String key, String value) {
+ String entry = key + "=" + value + "\n";
LanguageMap.inject(new ByteArrayInputStream(entry.getBytes(StandardCharsets.UTF_8)));
}
diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java
index 4ccd1b7..f4bf2f6 100644
--- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java
+++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java
@@ -17,10 +17,18 @@
import slimeknights.tconstruct.library.tools.DualToolHarvestUtils;
import slimeknights.tconstruct.library.tools.TinkerToolCore;
+import slimeknights.tconstruct.library.utils.ToolHelper;
/**
- * Extends TiC's {@code shouldUseOffhand} to recognise GT + TiC tool combinations
- * in both directions (GT main / TiC off, TiC main / GT off).
+ * Bridges GT tools into TiC's {@code shouldUseOffhand} system for cross-hand combinations.
+ *
+ *
+ *
*/
@Mixin(value = DualToolHarvestUtils.class, remap = false)
public class MixinDualToolHarvestUtils {
@@ -36,12 +44,21 @@ private static void gtOffHandSupportState(EntityLivingBase entity, IBlockState s
ItemStack offhand = player.getHeldItemOffhand();
if (tool.isEmpty() || offhand.isEmpty() || state == null) return;
- boolean isCrossCombo = (tool.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) ||
- (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool);
- if (!isCrossCombo) return;
+ if (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool) {
+ if (state.getMaterial().isToolNotRequired()) {
+ float gtSpeed = offhand.getItem().getDestroySpeed(offhand, state);
+ float ticSpeed = ToolHelper.calcDigSpeed(tool, state);
+ if (gtSpeed > ticSpeed) cir.setReturnValue(true);
+ } else if (!ToolHelper.canHarvest(tool, state) && gtmt$gtCanHarvest(offhand, state)) {
+ cir.setReturnValue(true);
+ }
+ return;
+ }
- if (gtmt$shouldPreferOffhand(tool, offhand, state)) {
- cir.setReturnValue(true);
+ if (tool.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) {
+ if (gtmt$shouldPreferOffhand(tool, offhand, state)) {
+ cir.setReturnValue(true);
+ }
}
}
@@ -57,31 +74,48 @@ private static void gtOffHandSupportPos(EntityLivingBase entity, BlockPos pos,
ItemStack offhand = player.getHeldItemOffhand();
if (tool.isEmpty() || offhand.isEmpty() || state == null) return;
- boolean isCrossCombo = (tool.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) ||
- (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool);
- if (!isCrossCombo) return;
+ if (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool) {
+ if (state.getMaterial().isToolNotRequired()) {
+ float gtSpeed = offhand.getItem().getDestroySpeed(offhand, state);
+ float ticSpeed = ToolHelper.calcDigSpeed(tool, state);
+ if (gtSpeed > ticSpeed) cir.setReturnValue(true);
+ } else if (!ToolHelper.canHarvest(tool, state) && gtmt$gtCanHarvest(offhand, state)) {
+ cir.setReturnValue(true);
+ }
+ return;
+ }
- if (gtmt$shouldPreferOffhand(tool, offhand, state)) {
- cir.setReturnValue(true);
+ if (tool.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) {
+ if (gtmt$shouldPreferOffhand(tool, offhand, state)) {
+ cir.setReturnValue(true);
+ }
}
}
@Unique
- private static boolean gtmt$shouldPreferOffhand(ItemStack main, ItemStack offhand, IBlockState state) {
+ private static boolean gtmt$gtCanHarvest(ItemStack gt, IBlockState state) {
if (state.getMaterial().isToolNotRequired()) return false;
Block block = state.getBlock();
String toolType = block.getHarvestTool(state);
if (toolType == null) return false;
+ int level = gt.getItem().getHarvestLevel(gt, toolType, null, state);
+ return level >= 0 && level >= block.getHarvestLevel(state);
+ }
+
+ @Unique
+ private static boolean gtmt$shouldPreferOffhand(ItemStack main, ItemStack offhand, IBlockState state) {
+ if (state.getMaterial().isToolNotRequired()) {
+ return offhand.getItem().getDestroySpeed(offhand, state) > main.getItem().getDestroySpeed(main, state);
+ }
+ Block block = state.getBlock();
+ String toolType = block.getHarvestTool(state);
+ if (toolType == null) return false;
int requiredLevel = block.getHarvestLevel(state);
int mainRaw = main.getItem().getHarvestLevel(main, toolType, null, state);
int offRaw = offhand.getItem().getHarvestLevel(offhand, toolType, null, state);
- // Offhand can harvest, main cannot.
if (requiredLevel >= 0 && offRaw >= requiredLevel && mainRaw < requiredLevel) return true;
-
- // Main is wrong type, offhand is correct type — prefer the appropriate tool
- // even when neither can produce drops (mirrors TiC+TiC cross-type behaviour).
if (mainRaw < 0 && offRaw >= 0) return true;
return false;
diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinEntityPlayer.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinEntityPlayer.java
index 4a9efbc..4f12cb4 100644
--- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinEntityPlayer.java
+++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinEntityPlayer.java
@@ -15,9 +15,8 @@
import slimeknights.tconstruct.library.utils.ToolHelper;
/**
- * Extends {@code EntityPlayer.canHarvestBlock} to consider the TiC off-hand tool
- * when the GT main-hand tool's {@code getHarvestLevel} returns -1 (wrong tool type)
- * and {@code ForgeHooks.canHarvestBlock} falls back to this method.
+ * Extends {@code EntityPlayer.canHarvestBlock} to pass when the TiC off-hand can harvest
+ * a block that the GT main-hand cannot.
*/
@Mixin(EntityPlayer.class)
public abstract class MixinEntityPlayer {
diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java
index 6f10314..87b74b3 100644
--- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java
+++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java
@@ -19,9 +19,8 @@
import slimeknights.tconstruct.library.utils.ToolHelper;
/**
- * Allows GT main-hand + TiC off-hand to pass {@code ForgeHooks.canHarvestBlock}.
- * ForgeHooks loads before MixinBooter's late phase, so this Mixin may not apply
- * in all environments; {@link MixinItemGTTool} is the primary coverage.
+ * Extends {@code ForgeHooks.canHarvestBlock} to pass when the TiC off-hand can harvest
+ * a block that the GT main-hand cannot.
*/
@Mixin(value = ForgeHooks.class, remap = false)
public class MixinForgeHooks {