package wtf.kity.uncrackable.mixin; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.DragonEggBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; 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.CallbackInfo; @Mixin(FallingBlockEntity.class) public abstract class FallingBlockEntityMixin extends Entity { public FallingBlockEntityMixin(EntityType entityType, Level level) { super(entityType, level); } /// If the egg falls into the void, teleport back up to build limit. @Inject(method = "tick", at = @At("HEAD")) private void beforeTick(CallbackInfo ci) { Vec3 pos = this.position(); if (pos.y < this.level().getMinBuildHeight()) { this.teleportTo(pos.x, this.level().getMaxBuildHeight(), pos.z); } } /// Egg should never time out. Skip add-assign operation entirely for the egg. @WrapOperation( method = "tick", at = @At( value = "FIELD", target = "Lnet/minecraft/world/entity/item/FallingBlockEntity;time:I", opcode = Opcodes.PUTFIELD ) ) private void setTime(FallingBlockEntity instance, int value, Operation original) { if (instance.getBlockState().getBlock() instanceof DragonEggBlock) { return; } original.call(instance, value); } /// Egg should not drop as an item if it lands above build height. @WrapOperation( method = "tick", at = @At( value = "FIELD", target = "Lnet/minecraft/world/entity/item/FallingBlockEntity;dropItem:Z", opcode = Opcodes.GETFIELD ) ) private boolean getDropItem(FallingBlockEntity instance, Operation original) { if (instance.getBlockState().getBlock() instanceof DragonEggBlock) { return false; } return original.call(instance); } /// If it lands inside a block, destroy the block as if by piston push. /// We override block shapes for non-piston-destructible blocks in`BlockBehaviorMixin` /// so normally this only applies to piston-destructible blocks, unless a block is pushed /// into the egg while it's an entity. @WrapOperation( method = "tick", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;canBeReplaced(Lnet/minecraft/world/item/context/BlockPlaceContext;)Z" ) ) private boolean canBeReplaced( BlockState instance, BlockPlaceContext blockPlaceContext, Operation original, @Local Block block, @Local BlockPos blockPos ) { if (original.call(instance, blockPlaceContext)) { return true; } if (block instanceof DragonEggBlock) { @Nullable BlockEntity blockentity = this.level().getBlockEntity(blockPos); Block.dropResources(instance, this.level(), blockPos, blockentity); instance.onDestroyedByPushReaction(this.level(), blockPos, Direction.DOWN, this.level().getFluidState(blockPos)); return true; } return false; } /// The egg should never drop as an item, so we always pretend that the ground beneath it is landable. @WrapOperation( method = "tick", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/level/block/FallingBlock;isFree(Lnet/minecraft/world/level/block/state/BlockState;)Z" ) ) private boolean isFree(BlockState state, Operation original, @Local Block block) { return original.call(state) && block instanceof DragonEggBlock; } }