public class HammerHandler implements Listener {
// haute priorité : on laisse les autres plugins passer avant,
// leur laissant le champ libre pour annuler l'évènement ;
// si un plugin de protection (WorldGuard) est présent, il annulera l'évènement,
// et notre fonction ne sera jamais appelée : si le bloc initial n'est pas détruit,
// ceux autours ne doivent pas l'être.
@EventHandler(priority = EventPriority.HIGHEST)
public void onBlockBreak(BlockBreakEvent e) {
Player player = e.getPlayer();
ItemStack tool = player.getInventory().getItemInMainHand();
if(!isMyHammer(tool)) {
return; // pas notre outil
}
Block block = e.getBlock();
final double breakRange = player.getGameMode() == GameMode.CREATIVE ? 5 : 4.5;
Location playerPos = player.getLocation();
Location eyePos = player.getEyeLocation();
RayTraceResult ray = player.getWorld().rayTraceBlocks(eyePos, playerPos.getDirection(), breakRange, FluidCollisionMode.NEVER, true);
BlockFace face;
if(ray == null || ray.getHitBlock() == null || !ray.getHitBlock().equals(block) || (face = ray.getHitBlockFace()) == null) {
return;
}
// l'axe de la face
Axis faceAxis = getAxis(face);
// la configuration de l'outil
final int width = 5; // cinq blocs de longueur (à droite/à gauche du bloc)
final int height = 3; // trois blocs de hauteur (en haut/en bas du bloc)
final int depth = 1; // un bloc de profondeur (devant/derrière le bloc)
// on récupère nos trois axes relatifs à notre longueur/hauteur/profondeur
//noinspection UnnecessaryLocalVariable
Axis depthAxis = faceAxis;
Axis heightAxis = switch(faceAxis) {
// le joueur mine à l'horizontal, la hauteur est forcément l'axe Y
case X, Z -> Axis.Y;
case Y -> {
// le joueur mine le sol ou le ciel,
// l'axe Y représente la profondeur (devant/derrière) donc
// il faut prendre un autre axe à partir de la direction du joueur
// 0°: +z
// 90°: +x
// 180°: -z
// 270°: -x
double xRot = playerPos.getYaw(); // -180 <= xRot < 180
if(Math.signum(xRot) == -1.0) {
xRot += 360;
}
// 0 <= xRot < 360
// 90° pour chaque direction, à +/- 45° :
// -45° -> 45°: +z
// 45° -> 135°: +x
// 135° -> 225°: -z
// 225° -> 315°: -x
// donc:
// 45° -> 135° et 225° -> 315°: x
// 315° -> 45° et 135° -> 225°: z
if((xRot >= 45 && xRot < 135) || (xRot >= 225 && xRot < 315)) {
yield Axis.X; // le corps du joueur est orienté vers l'axe X
}
else {
yield Axis.Z; // le corps du joueur est orienté vers l'axe Z
}
}
};
Axis widthAxis = switch(faceAxis) {
// la profondeur est l'axe X,
// la hauteur est l'axe Y,
// donc il ne reste plus que Z.
case X -> Axis.Z;
// la profondeur est l'axe Y,
// la hauteur est l'axe X ou l'axe Z,
// donc prenons le seul axe restant.
case Y -> (heightAxis == Axis.X) ? Axis.Z : Axis.X;
// la profondeur est l'axe Z,
// la hauteur est l'axe Y,
// donc il ne reste plus que X.
case Z -> Axis.X;
};
// après les maths, le trou.
final int startWidth = -(width / 2);
final int endWidth = width + startWidth - 1;
final int startHeight = -(height / 2);
final int endHeight = height + startHeight - 1;
final int startDepth = 0;
final int endDepth = depth - 1;
for(int w = startWidth; w <= endWidth; ++w) {
for(int h = startHeight; h <= endHeight; ++h) {
for(int d = startDepth; d <= endDepth; ++d) {
int x = 0, y = 0, z = 0;
switch(widthAxis) {
case X -> x = w;
case Y -> y = w;
case Z -> z = w;
}
switch(heightAxis) {
case X -> x = h;
case Y -> y = h;
case Z -> z = h;
}
switch(depthAxis) {
case X -> x = d;
case Y -> y = d;
case Z -> z = d;
}
Block relative = block.getRelative(x, y, z);
if(relative.getType() != Material.BEDROCK) {
// enlève le bloc et drop ses loots,
// DROP UNIQUEMENT si `tool` est le bon outil
// (enlève toujours le bloc même si `tool` n'est pas le bon outil)
relative.breakNaturally(tool);
}
}
}
}
}
// mettre qqch dans l'item pour empêcher les joueurs de l'avoir en vanilla, typiquement une couleur,
// mais dans ce cas il faut empêcher les joueurs de renommer notre outil
private static boolean isMyHammer(@Nullable ItemStack tool) {
ItemMeta meta;
//noinspection ConstantConditions
return tool != null && tool.getType() == Material.NETHERITE_PICKAXE && tool.hasItemMeta() && (meta = tool.getItemMeta()).hasDisplayName() && meta.getDisplayName().equals("Variegatus");
}
@NotNull // org.bukkit.Axis
public static Axis getAxis(BlockFace face) {
return switch(face) {
case EAST, WEST -> Axis.X;
case UP, DOWN, SELF -> Axis.Y;
case NORTH, SOUTH -> Axis.Z;
default -> throw new IllegalArgumentException("non-cartesian face: " + face.name());
};
}
}