filename:
common/src/main/java/rearth/oritech/item/tools/harvesting/PromethiumPickaxeItem.java
branch:
1.21
back to repo
package rearth.oritech.item.tools.harvesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import dev.architectury.event.EventResult;
import dev.architectury.utils.value.IntValue;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.item.BuiltinModelItemRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.AttributeModifierSlot;
import net.minecraft.component.type.AttributeModifiersComponent;
import net.minecraft.component.type.ItemEnchantmentsComponent;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.MiningToolItem;
import net.minecraft.item.ToolMaterial;
import net.minecraft.item.tooltip.TooltipType;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.Unit;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import rearth.oritech.Oritech;
import rearth.oritech.client.renderers.PromethiumToolRenderer;
import rearth.oritech.init.ComponentContent;
import rearth.oritech.init.TagContent;
import rearth.oritech.init.ToolsContent;
import software.bernie.geckolib.animatable.GeoItem;
import software.bernie.geckolib.animatable.SingletonGeoAnimatable;
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
import java.util.List;
import java.util.function.Consumer;
public class PromethiumPickaxeItem extends MiningToolItem implements GeoItem {
private static final RawAnimation AREA_ANIM = RawAnimation.begin().thenLoop("area");
private static final RawAnimation SILK_ANIM = RawAnimation.begin().thenLoop("silk_touch");
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public PromethiumPickaxeItem(ToolMaterial toolMaterial, TagKey<Block> effectiveBlocks, Settings settings) {
super(toolMaterial, effectiveBlocks, settings);
SingletonGeoAnimatable.registerSyncedAnimatable(this);
}
@Override
public boolean canRepair(ItemStack stack, ItemStack ingredient) {
return false;
}
@Override
public boolean isEnchantable(ItemStack stack) {
return true;
}
@Override
public boolean postMine(ItemStack stack, World world, BlockState state, BlockPos pos, LivingEntity miner) {
if (!world.isClient && stack.contains(DataComponentTypes.INTANGIBLE_PROJECTILE)) {
var enchantments = stack.getEnchantments();
var builder = new ItemEnchantmentsComponent.Builder(enchantments);
builder.remove(elem -> elem.matchesKey(Enchantments.SILK_TOUCH));
stack.set(DataComponentTypes.ENCHANTMENTS, builder.build());
}
return true;
}
private static boolean isAreaEnabled(ItemStack stack) {
return stack.getOrDefault(ComponentContent.IS_AOE_ACTIVE.get(), false);
}
private static void setAreaEnabled(ItemStack stack, boolean enabled) {
stack.set(ComponentContent.IS_AOE_ACTIVE.get(), enabled);
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
if (!world.isClient && user.isSneaking()) {
var stack = user.getStackInHand(hand);
var wasArea = isAreaEnabled(stack);
var isArea = !wasArea;
setAreaEnabled(stack, isArea);
user.sendMessage(isArea ? Text.translatable("message.oritech.tool_mode.area_effect") : Text.translatable("message.oritech.tool_mode.silk_touch"));
}
return super.use(world, user, hand);
}
public static List<BlockPos> getOffsetBlocks(World world, PlayerEntity player, BlockPos pos) {
var handStack = player.getMainHandStack();
if (handStack == null || !handStack.isOf(ToolsContent.PROMETHIUM_PICKAXE)) return List.of();
if (isAreaEnabled(handStack) && !player.isSneaking()) {
List<BlockPos> breakBlocks;
var playerHit = player.raycast(player.getBlockInteractionRange(), 0.0F, false);
if (playerHit instanceof BlockHitResult blockHit) {
var blockSide = blockHit.getSide();
if (blockSide == Direction.UP || blockSide == Direction.DOWN) {
var direction = player.getHorizontalFacing();
if (direction == Direction.NORTH || direction == Direction.SOUTH) {
breakBlocks = List.of(pos.north(), pos.south());
} else {
breakBlocks = List.of(pos.east(), pos.west());
}
} else {
breakBlocks = List.of(pos.up(), pos.down());
}
return ImmutableList.copyOf(Iterables.filter(breakBlocks, p -> world.getBlockState(p).isIn(TagContent.DRILL_MINEABLE)));
}
}
return List.of();
}
// called as event in Oritech initializer
// area mode: breaks 3x1 blocks unless player is sneaking
// silk touch mode: adds a temporary silk touch, which is then removed in the after break event
public static EventResult preMine(World world, BlockPos pos, BlockState state, ServerPlayerEntity player, @Nullable IntValue xp) {
var handStack = player.getMainHandStack();
if (handStack == null || !handStack.isOf(ToolsContent.PROMETHIUM_PICKAXE)) return EventResult.pass();
// break additional blocks in preMine (Block.onBreak) instead of postMine (Block.onBroken)
// so that the block still exists when determining which face of the block the player was looking at
if (isAreaEnabled(handStack)) {
// break additional blocks
for (var offsetPos : getOffsetBlocks(world, player, pos)) {
// drop stacks before breaking additional block, because world.breakBlock doesn't apply item enchantments if drop is enabled
// this will ONLY apply item enchantments that affect block drops, and will not apply enchants like vein mining
var offsetState = world.getBlockState(offsetPos);
var offsetEntity = world.getBlockEntity(offsetPos);
Block.dropStacks(offsetState, world, offsetPos, offsetEntity, player, handStack);
offsetState.getBlock().onBreak(world, offsetPos, offsetState, player);
world.breakBlock(offsetPos, false, player);
}
} else {
// do silk touch
var hasExistingSilkTouch = EnchantmentHelper.getEnchantments(handStack).getEnchantments().stream().anyMatch(elem -> elem.matchesKey(Enchantments.SILK_TOUCH));
if (!hasExistingSilkTouch) {
var registryEntry = world.getRegistryManager().get(RegistryKeys.ENCHANTMENT).getEntry(Enchantments.SILK_TOUCH).get();
handStack.addEnchantment(registryEntry, 1);
handStack.set(DataComponentTypes.INTANGIBLE_PROJECTILE, Unit.INSTANCE);
}
}
return EventResult.pass();
}
@Override
public AttributeModifiersComponent getAttributeModifiers() {
return super.getAttributeModifiers()
.with(EntityAttributes.PLAYER_BLOCK_INTERACTION_RANGE, new EntityAttributeModifier(Oritech.id("pick_block_range"), 2, EntityAttributeModifier.Operation.ADD_VALUE), AttributeModifierSlot.MAINHAND)
.with(EntityAttributes.PLAYER_ENTITY_INTERACTION_RANGE, new EntityAttributeModifier(Oritech.id("pick_entity_range"), 2, EntityAttributeModifier.Operation.ADD_VALUE), AttributeModifierSlot.MAINHAND);
}
@Override
public void appendTooltip(ItemStack stack, TooltipContext context, List<Text> tooltip, TooltipType type) {
super.appendTooltip(stack, context, tooltip, type);
var area = isAreaEnabled(stack);
tooltip.add((area ? Text.translatable("tooltip.oritech.tool_mode.area_range.area") : Text.translatable("tooltip.oritech.tool_mode.area_range.single")).formatted(Formatting.GOLD));
tooltip.add(Text.translatable("tooltip.oritech.promethium_pick").formatted(Formatting.DARK_GRAY));
}
@Override
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
consumer.accept(new GeoRenderProvider() {
private PromethiumToolRenderer renderer;
@Override
public @Nullable BuiltinModelItemRenderer getGeoItemRenderer() {
if (this.renderer == null)
this.renderer = new PromethiumToolRenderer("promethium_pickaxe");
return renderer;
}
});
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Pickaxe", 5, state -> PlayState.CONTINUE).triggerableAnim("silk", SILK_ANIM).triggerableAnim("area", AREA_ANIM));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return cache;
}
// client only
public void onHeldTick(ItemStack stack, PlayerEntity player, ClientWorld world) {
if (world.getTime() % 20 != 0) return;
var area = isAreaEnabled(stack);
triggerAnim(player, GeoItem.getId(stack), "Pickaxe", area ? "area" : "silk");
}
}