diff --git a/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/GTToolType.java b/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/GTToolType.java index d2cf8c523b..4dc3d8f42e 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/GTToolType.java +++ b/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/GTToolType.java @@ -27,7 +27,7 @@ public enum GTToolType { AXE("axe", "axes", 6.0F, -3.2F, true), HOE("hoe", "hoes", 0, -3.0F, true), SAW("saw", "saws", 1, 1, GTSoundEntries.SAW_TOOL), - HARD_HAMMER("hammer", "hammers", 1, 1, GTSoundEntries.FORGE_HAMMER), + HARD_HAMMER("hammer", "hammers", TagUtil.createBlockTag("mineable/pickaxe", true), 1.5F, -3.2F, GTCEu.id("item/tools/hammer"), GTSoundEntries.FORGE_HAMMER), SOFT_MALLET("mallet", "mallets", 1, 1, GTSoundEntries.SOFT_MALLET_TOOL), WRENCH("wrench", "wrenches", 1, 1, GTSoundEntries.WRENCH_TOOL), FILE("file", "files", 1, 1, GTSoundEntries.FILE_TOOL), diff --git a/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/ToolHelper.java b/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/ToolHelper.java index b7f61f40c1..0d344a1ce5 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/ToolHelper.java +++ b/common/src/main/java/com/gregtechceu/gtceu/api/item/tool/ToolHelper.java @@ -4,12 +4,25 @@ import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.item.GTToolItem; +import com.lowdragmc.lowdraglib.utils.RayTraceHelper; +import com.mojang.math.Vector3d; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; /** * @author KilaBash @@ -53,4 +66,65 @@ public static void playToolSound(GTToolType toolType, ServerPlayer player) { } } + public static List getAOEPositions(LivingEntity miner, ItemStack stack, BlockPos pos, int radius) { + + Level level = miner.getLevel(); + + ArrayList aoePositions = new ArrayList<>(); + ArrayList potentialPositions = new ArrayList<>(); + + + for(int x = -radius; x <= radius; x++) { + for(int y = -radius; y <= radius; y++) { + for(int z = -radius; z <= radius; z++) { + potentialPositions.add(new BlockPos(x, y, z)); + } + } + } + + Vec3 cameraPos = miner.getEyePosition(1); + Vec3 rotation = miner.getViewVector(1); + + Vec3 combined = cameraPos.add(rotation.x * 4.5F, rotation.y * 4.5F, rotation.z * 4.5F); + + BlockHitResult result = level.clip(new ClipContext(cameraPos, combined, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, miner)); + + + for (BlockPos blockPos : potentialPositions) { + + switch (result.getDirection().getAxis()) { + case X -> { + if(blockPos.getX() == 0) { + aoePositions.add(pos.offset(blockPos)); + } + } + case Y -> { + if(blockPos.getY() == 0) { + aoePositions.add(pos.offset(blockPos)); + } + } + case Z -> { + if(blockPos.getZ() == 0) { + aoePositions.add(pos.offset(blockPos)); + } + } + } + } + + return aoePositions; + } + + public static boolean aoeCanBreak(ItemStack stack, Level level, BlockPos origin, BlockPos pos) { + if (origin.equals(pos)) return false; + if (!stack.isCorrectToolForDrops(level.getBlockState(pos))) return false; + + BlockState state = level.getBlockState(pos); + BlockState originState = level.getBlockState(origin); + + //Adapted from GTCEU 1.12.2, used to stop mining blocks like obsidian faster by targeting neighbouring stone. The value 8 is an arbitrary and does not represent anything. + if (state.getDestroySpeed(level, pos) < 0 || state.getDestroySpeed(level, pos) - originState.getDestroySpeed(level, pos) > 8) return false; + + return true; + } + } diff --git a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelRendererMixin.java b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelRendererMixin.java new file mode 100644 index 0000000000..41c4c64a8f --- /dev/null +++ b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelRendererMixin.java @@ -0,0 +1,131 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.api.item.tool.ToolHelper; +import com.gregtechceu.gtceu.data.recipe.CustomTags; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexMultiConsumer; +import com.mojang.math.Matrix4f; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.BlockDestructionProgress; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; +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; +import java.util.List; +import java.util.SortedSet; + +@Mixin(LevelRenderer.class) +@Environment(EnvType.CLIENT) +public abstract class LevelRendererMixin { + + @Shadow @Final private Minecraft minecraft; + + @Shadow @Final private Long2ObjectMap> destructionProgress; + + @Shadow @Final private RenderBuffers renderBuffers; + + @Inject( + method = {"renderLevel"}, + at = {@At("HEAD")} + ) + private void renderLevel(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) { + if (minecraft.player == null || minecraft.level == null) return; + + ItemStack mainHandItem = minecraft.player.getMainHandItem(); + + if (!mainHandItem.is(CustomTags.AOE_TOOLS) || !(minecraft.hitResult instanceof BlockHitResult result) || minecraft.player.isCrouching()) return; + + BlockPos hitResultPos = result.getBlockPos(); + BlockState hitResultState = minecraft.level.getBlockState(hitResultPos); + + SortedSet progresses = destructionProgress.get(hitResultPos.asLong()); + + if (progresses == null || progresses.isEmpty() || !mainHandItem.isCorrectToolForDrops(hitResultState)) return; + + BlockDestructionProgress progress = progresses.last(); + + List positions = ToolHelper.getAOEPositions(minecraft.player, mainHandItem, hitResultPos, 1); + + Vec3 vec3 = camera.getPosition(); + double d = vec3.x(); + double e = vec3.y(); + double f = vec3.z(); + + for (BlockPos pos : positions) { + poseStack.pushPose(); + poseStack.translate((double)pos.getX() - d, (double)pos.getY() - e, (double)pos.getZ() - f); + PoseStack.Pose pose2 = poseStack.last(); + VertexConsumer vertexConsumer2 = new SheetedDecalTextureGenerator( + this.renderBuffers.crumblingBufferSource().getBuffer((RenderType)ModelBakery.DESTROY_TYPES.get(progress.getProgress())), pose2.pose(), pose2.normal() + ); + this.minecraft.getBlockRenderer().renderBreakingTexture(minecraft.level.getBlockState(pos), pos, minecraft.level, poseStack, vertexConsumer2); + poseStack.popPose(); + } + } + + @Invoker("renderShape") + public static void renderShape(PoseStack poseStack, VertexConsumer consumer, VoxelShape shape, double x, double y, double z, float red, float green, float blue, float alpha) { + throw new AssertionError(); + } + + @Inject( + method = {"renderHitOutline"}, + at = {@At("HEAD")} + ) + private void renderHitOutline(PoseStack poseStack, VertexConsumer consumer, Entity entity, double camX, double camY, double camZ, BlockPos pos, BlockState state, CallbackInfo ci) { + if (minecraft.player == null || minecraft.level == null) return; + + ItemStack mainHandItem = minecraft.player.getMainHandItem(); + + if (!mainHandItem.is(CustomTags.AOE_TOOLS) || state.isAir() || !minecraft.level.isInWorldBounds(pos) || !mainHandItem.isCorrectToolForDrops(state) || minecraft.player.isCrouching()) return; + + List blockPositions = ToolHelper.getAOEPositions(minecraft.player, mainHandItem, pos, 1); + List outlineShapes = new ArrayList<>(); + + for (BlockPos position : blockPositions) { + if (!ToolHelper.aoeCanBreak(mainHandItem, minecraft.level, pos, position)) continue; + + BlockPos diffPos = position.subtract(pos); + BlockState offsetState = minecraft.level.getBlockState(position); + + outlineShapes.add(offsetState.getShape(minecraft.level, position).move(diffPos.getX(), diffPos.getY(), diffPos.getZ())); + } + + outlineShapes.forEach(shape -> { + renderShape( + poseStack, + consumer, + shape, + (double) pos.getX() - camX, + (double) pos.getY() - camY, + (double) pos.getZ() - camZ, + 0.0F, + 0.0F, + 0.0F, + 0.4F); + }); + + } +} diff --git a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/MultiPlayerGameModeMixin.java b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/MultiPlayerGameModeMixin.java index 3041459809..a0c018d18f 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/MultiPlayerGameModeMixin.java +++ b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/MultiPlayerGameModeMixin.java @@ -1,14 +1,24 @@ package com.gregtechceu.gtceu.core.mixins; +import com.gregtechceu.gtceu.api.data.tag.TagUtil; import com.gregtechceu.gtceu.api.item.IItemUseFirst; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.data.recipe.CustomTags; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.tags.TagKey; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.Item; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +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.CallbackInfoReturnable; @@ -20,6 +30,8 @@ */ @Mixin(MultiPlayerGameMode.class) public class MultiPlayerGameModeMixin { + @Shadow @Final private Minecraft minecraft; + @Inject( method = {"performUseItemOn"}, at = {@At( @@ -38,4 +50,27 @@ public class MultiPlayerGameModeMixin { } } } + + @Inject( + method = {"destroyBlock"}, + at = {@At("HEAD")}, + cancellable = true + ) + private void destroyBlock(BlockPos pos, CallbackInfoReturnable cir) { + if ( + minecraft.player == null || + minecraft.level == null || + !minecraft.player.getMainHandItem().is(CustomTags.AOE_TOOLS) || + minecraft.player.isCrouching() || + !minecraft.player.getMainHandItem().isCorrectToolForDrops(minecraft.level.getBlockState(pos)) + ) return; + + cir.cancel(); + Level level = minecraft.level; + + if (level == null) return; + BlockState state = level.getBlockState(pos); + + state.getBlock().destroy(level, pos, state); + } } diff --git a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerPlayerGameModeMixin.java b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerPlayerGameModeMixin.java index 7e7ce06165..ebb186b6e9 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerPlayerGameModeMixin.java +++ b/common/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerPlayerGameModeMixin.java @@ -1,20 +1,33 @@ package com.gregtechceu.gtceu.core.mixins; import com.gregtechceu.gtceu.api.item.IItemUseFirst; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.api.item.tool.ToolHelper; +import com.gregtechceu.gtceu.data.recipe.CustomTags; +import net.minecraft.client.particle.AshParticle; +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +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.CallbackInfoReturnable; +import java.util.List; + /** * @author KilaBash * @date 2023/2/24 @@ -22,6 +35,12 @@ */ @Mixin({ServerPlayerGameMode.class}) public class ServerPlayerGameModeMixin { + + @Final + @Shadow protected ServerPlayer player; + + @Shadow protected ServerLevel level; + @Inject( method = {"useItemOn"}, at = {@At( @@ -42,4 +61,28 @@ public class ServerPlayerGameModeMixin { } } } + + @Inject( + method = {"destroyBlock"}, + at = {@At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/Block;playerWillDestroy(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/player/Player;)V" + )} + ) + private void destroyBlock(BlockPos pos, CallbackInfoReturnable cir) { + ItemStack mainHandItem = player.getMainHandItem(); + + if (mainHandItem.is(CustomTags.AOE_TOOLS) && mainHandItem.isCorrectToolForDrops(level.getBlockState(pos)) && !player.isCrouching()) { + List blockPosList = ToolHelper.getAOEPositions(player, player.getMainHandItem(), pos, 1); + + BlockState originBlock = level.getBlockState(pos); + + for (BlockPos blockPos : blockPosList) { + if (!ToolHelper.aoeCanBreak(mainHandItem, level, pos, blockPos)) continue; + level.destroyBlock(blockPos, true, player); + if (mainHandItem.hurt(1, RandomSource.create(), player)) break; + } + } + + } } diff --git a/common/src/main/java/com/gregtechceu/gtceu/data/recipe/CustomTags.java b/common/src/main/java/com/gregtechceu/gtceu/data/recipe/CustomTags.java index 14d16fe47d..7e64141e15 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/data/recipe/CustomTags.java +++ b/common/src/main/java/com/gregtechceu/gtceu/data/recipe/CustomTags.java @@ -47,6 +47,8 @@ public class CustomTags { public static final TagKey UV_BATTERIES = TagUtil.createPlatformItemTag("batteries/uv", "uv_batteries"); public static final TagKey UHV_BATTERIES = TagUtil.createPlatformItemTag("batteries/uhv", "uhv_batteries"); + public static final TagKey AOE_TOOLS = TagUtil.createPlatformItemTag("tools/aoe", "aoe_tools"); + // Platform-dependent tags public static final TagKey TAG_WOODEN_CHESTS = TagUtil.createPlatformItemTag("chests/wooden", "chests"); diff --git a/common/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/ToolRecipeHandler.java b/common/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/ToolRecipeHandler.java index ad1944ef29..733e20b881 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/ToolRecipeHandler.java +++ b/common/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/ToolRecipeHandler.java @@ -164,6 +164,11 @@ private static void processTool(TagPrefix prefix, Material material, ToolPropert 'I', ingot, 'S', stick); + addToolRecipe(provider, material, GTToolType.HARD_HAMMER, true, + "PPf", "PPS", "PPh", + 'P', plate, + 'S', stick); + addToolRecipe(provider, material, GTToolType.FILE, true, " P ", " P " , " S ", 'P', plate, diff --git a/common/src/main/java/com/gregtechceu/gtceu/data/tags/ItemTagLoader.java b/common/src/main/java/com/gregtechceu/gtceu/data/tags/ItemTagLoader.java index d6be66bf65..61294a2c2a 100644 --- a/common/src/main/java/com/gregtechceu/gtceu/data/tags/ItemTagLoader.java +++ b/common/src/main/java/com/gregtechceu/gtceu/data/tags/ItemTagLoader.java @@ -1,17 +1,24 @@ package com.gregtechceu.gtceu.data.tags; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.chemical.material.MarkerMaterials.Color; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.data.tag.TagUtil; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.data.recipe.CustomTags; import com.tterrag.registrate.providers.RegistrateTagsProvider; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagBuilder; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + import static com.gregtechceu.gtceu.api.data.tag.TagPrefix.*; import static com.gregtechceu.gtceu.common.data.GTMaterials.*; @@ -21,6 +28,9 @@ public static void init(RegistrateTagsProvider provider) { create(provider, lens, Color.White, GTItems.MATERIAL_ITEMS.get(lens, Glass).getId()); create(provider, "pistons", rl("piston"), rl("sticky_piston")); + create(provider, CustomTags.AOE_TOOLS, GTItems.TOOL_ITEMS.column(GTToolType.HARD_HAMMER).values().stream().filter(Objects::nonNull).map(entry -> GTCEu.id(entry.get().getTier().material.getName() + "_hammer")).toList().toArray(new ResourceLocation[0])); + + // TODO add to planks mc tag? //for (Material material : new Material[]{GTMaterials.Wood, GTMaterials.TreatedWood}) { // for (ItemLike woodPlateStack : ChemicalHelper.getItems(new UnificationEntry(TagPrefix.plate, material))) { diff --git a/common/src/main/resources/gtceu-common.mixins.json b/common/src/main/resources/gtceu-common.mixins.json index a591e0c2e4..b7dffe7366 100644 --- a/common/src/main/resources/gtceu-common.mixins.json +++ b/common/src/main/resources/gtceu-common.mixins.json @@ -6,6 +6,7 @@ "plugin": "com.gregtechceu.gtceu.core.mixins.GregTechMixinPlugin", "client": [ "BlockModelMixin", + "LevelRendererMixin", "ModelBakeryMixin", "MultiPlayerGameModeMixin" ], diff --git a/fabric/src/generated/resources/data/c/tags/items/aoe_tools.json b/fabric/src/generated/resources/data/c/tags/items/aoe_tools.json new file mode 100644 index 0000000000..850ff8c700 --- /dev/null +++ b/fabric/src/generated/resources/data/c/tags/items/aoe_tools.json @@ -0,0 +1,28 @@ +{ + "replace": false, + "values": [ + "gtceu:aluminium_hammer", + "gtceu:iron_hammer", + "gtceu:titanium_hammer", + "gtceu:neutronium_hammer", + "gtceu:duranium_hammer", + "gtceu:bronze_hammer", + "gtceu:diamond_hammer", + "gtceu:invar_hammer", + "gtceu:sterling_silver_hammer", + "gtceu:rose_gold_hammer", + "gtceu:stainless_steel_hammer", + "gtceu:steel_hammer", + "gtceu:ultimet_hammer", + "gtceu:wrought_iron_hammer", + "gtceu:tungsten_carbide_hammer", + "gtceu:damascus_steel_hammer", + "gtceu:tungsten_steel_hammer", + "gtceu:cobalt_brass_hammer", + "gtceu:vanadium_steel_hammer", + "gtceu:naquadah_alloy_hammer", + "gtceu:red_steel_hammer", + "gtceu:blue_steel_hammer", + "gtceu:hsse_hammer" + ] +} \ No newline at end of file diff --git a/forge/src/generated/resources/data/forge/tags/items/tools/aoe.json b/forge/src/generated/resources/data/forge/tags/items/tools/aoe.json new file mode 100644 index 0000000000..082a90c082 --- /dev/null +++ b/forge/src/generated/resources/data/forge/tags/items/tools/aoe.json @@ -0,0 +1,27 @@ +{ + "values": [ + "gtceu:aluminium_hammer", + "gtceu:iron_hammer", + "gtceu:titanium_hammer", + "gtceu:neutronium_hammer", + "gtceu:duranium_hammer", + "gtceu:bronze_hammer", + "gtceu:diamond_hammer", + "gtceu:invar_hammer", + "gtceu:sterling_silver_hammer", + "gtceu:rose_gold_hammer", + "gtceu:stainless_steel_hammer", + "gtceu:steel_hammer", + "gtceu:ultimet_hammer", + "gtceu:wrought_iron_hammer", + "gtceu:tungsten_carbide_hammer", + "gtceu:damascus_steel_hammer", + "gtceu:tungsten_steel_hammer", + "gtceu:cobalt_brass_hammer", + "gtceu:vanadium_steel_hammer", + "gtceu:naquadah_alloy_hammer", + "gtceu:red_steel_hammer", + "gtceu:blue_steel_hammer", + "gtceu:hsse_hammer" + ] +} \ No newline at end of file