Skip to content

Commit

Permalink
Add mining hammers (#164)
Browse files Browse the repository at this point in the history
* add mining hammer tool type and recipe

* Basic 3x3 functionality

* Force server to break blocks instead of client

* Add extended block outline

* Render extra block destruction

* Fix sounds not playing when incorrect block broken

* Add AOE tools tag

* Extra rendering should not happen if player is crouching

* Replace old check with tag

* Give hardhammer AOE functionality instead of making new tool

* Fix sounds again

---------

Co-authored-by: KilaBash <[email protected]>
  • Loading branch information
TacoMonkey11 and Yefancy authored Jul 17, 2023
1 parent aab79fd commit 188d98e
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -53,4 +66,65 @@ public static void playToolSound(GTToolType toolType, ServerPlayer player) {
}
}

public static List<BlockPos> getAOEPositions(LivingEntity miner, ItemStack stack, BlockPos pos, int radius) {

Level level = miner.getLevel();

ArrayList<BlockPos> aoePositions = new ArrayList<>();
ArrayList<BlockPos> 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;
}

}
Original file line number Diff line number Diff line change
@@ -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<SortedSet<BlockDestructionProgress>> 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<BlockDestructionProgress> progresses = destructionProgress.get(hitResultPos.asLong());

if (progresses == null || progresses.isEmpty() || !mainHandItem.isCorrectToolForDrops(hitResultState)) return;

BlockDestructionProgress progress = progresses.last();

List<BlockPos> 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<BlockPos> blockPositions = ToolHelper.getAOEPositions(minecraft.player, mainHandItem, pos, 1);
List<VoxelShape> 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);
});

}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,6 +30,8 @@
*/
@Mixin(MultiPlayerGameMode.class)
public class MultiPlayerGameModeMixin {
@Shadow @Final private Minecraft minecraft;

@Inject(
method = {"performUseItemOn"},
at = {@At(
Expand All @@ -38,4 +50,27 @@ public class MultiPlayerGameModeMixin {
}
}
}

@Inject(
method = {"destroyBlock"},
at = {@At("HEAD")},
cancellable = true
)
private void destroyBlock(BlockPos pos, CallbackInfoReturnable<Boolean> 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);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
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
* @implNote ServerPlayerGameModeMixin
*/
@Mixin({ServerPlayerGameMode.class})
public class ServerPlayerGameModeMixin {

@Final
@Shadow protected ServerPlayer player;

@Shadow protected ServerLevel level;

@Inject(
method = {"useItemOn"},
at = {@At(
Expand All @@ -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<Boolean> cir) {
ItemStack mainHandItem = player.getMainHandItem();

if (mainHandItem.is(CustomTags.AOE_TOOLS) && mainHandItem.isCorrectToolForDrops(level.getBlockState(pos)) && !player.isCrouching()) {
List<BlockPos> 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;
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class CustomTags {
public static final TagKey<Item> UV_BATTERIES = TagUtil.createPlatformItemTag("batteries/uv", "uv_batteries");
public static final TagKey<Item> UHV_BATTERIES = TagUtil.createPlatformItemTag("batteries/uhv", "uhv_batteries");

public static final TagKey<Item> AOE_TOOLS = TagUtil.createPlatformItemTag("tools/aoe", "aoe_tools");

// Platform-dependent tags
public static final TagKey<Item> TAG_WOODEN_CHESTS = TagUtil.createPlatformItemTag("chests/wooden", "chests");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 188d98e

Please sign in to comment.