Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/Pylon.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.github.pylonmc.pylon.content.machines.smelting.Bloomery;
import io.github.pylonmc.pylon.content.talismans.*;
import io.github.pylonmc.pylon.content.tools.SoulboundRune;
import io.github.pylonmc.pylon.content.tools.TapeMeasure;
import io.github.pylonmc.pylon.content.tools.base.Rune;
import io.github.pylonmc.rebar.addon.RebarAddon;
import io.github.pylonmc.rebar.config.ConfigSection;
Expand Down Expand Up @@ -69,6 +70,7 @@ public void onEnable() {
pm.registerEvents(new HuntingTalisman.HuntingTalismanListener(), this);
pm.registerEvents(new ExperienceTalisman.XPTalismanListener(), this);
pm.registerEvents(new SleepingBag.PlaceListener(), this);
pm.registerEvents(new TapeMeasure.TapeMeasureListener(), this);

RebarRegistry.RESEARCHES.mapKey(pylonKey("simple_components"), pylonKey("components_1"));
RebarRegistry.RESEARCHES.mapKey(pylonKey("scientific_revolution_4"), pylonKey("scientific_revolution_3"));
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,15 @@ private PylonItems() {
PylonPages.TOOLS.addItem(CONFETTI_POPPER);
}

public static final ItemStack TAPE_MEASURE = ItemStackBuilder.rebar(Material.CLAY_BALL, PylonKeys.TAPE_MEASURE)
.set(DataComponentTypes.ITEM_MODEL, Material.IRON_NAUTILUS_ARMOR.getKey())
.set(DataComponentTypes.MAX_STACK_SIZE, 1)
.build();
static {
RebarItem.register(TapeMeasure.class, TAPE_MEASURE, PylonKeys.TAPE_MEASURE);
PylonPages.TOOLS.addItem(TAPE_MEASURE);
}

public static final ItemStack PALLADIUM_AXE = ItemStackBuilder.rebarToolWeapon(Material.DIAMOND_AXE, PylonKeys.PALLADIUM_AXE, RebarUtils.axeMineable(), true, true, true)
.set(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments()
.add(Enchantment.EFFICIENCY, ConfigSection.fromSettings(PylonKeys.PALLADIUM_AXE).getOrThrow("efficiency-level", ConfigAdapter.INTEGER))
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public class PylonKeys {

public static final NamespacedKey BRICK_MOLD = pylonKey("brick_mold");
public static final NamespacedKey CONFETTI_POPPER = pylonKey("confetti_popper");
public static final NamespacedKey TAPE_MEASURE = pylonKey("tape_measure");

public static final NamespacedKey MONSTER_JERKY = pylonKey("monster_jerky");

Expand Down
301 changes: 301 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/content/tools/TapeMeasure.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
package io.github.pylonmc.pylon.content.tools;

import io.github.pylonmc.pylon.Pylon;
import io.github.pylonmc.pylon.util.PylonUtils;
import io.github.pylonmc.rebar.config.adapter.ConfigAdapter;
import io.github.pylonmc.rebar.entity.display.ItemDisplayBuilder;
import io.github.pylonmc.rebar.entity.display.TextDisplayBuilder;
import io.github.pylonmc.rebar.entity.display.transform.LineBuilder;
import io.github.pylonmc.rebar.entity.display.transform.TransformBuilder;
import io.github.pylonmc.rebar.item.RebarItem;
import io.github.pylonmc.rebar.item.interfaces.InteractRebarItemHandler;
import io.github.pylonmc.rebar.util.gui.unit.UnitFormat;
import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Display;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3d;
import org.jspecify.annotations.NonNull;

import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


public class TapeMeasure extends RebarItem implements InteractRebarItemHandler {

public final Material material = getSettings().getOrThrow("material", ConfigAdapter.MATERIAL);
public final Material finishedMaterial = getSettings().getOrThrow("finished-material", ConfigAdapter.MATERIAL);
public final double thickness = getSettings().getOrThrow("thickness", ConfigAdapter.DOUBLE);
public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER);

public TapeMeasure(@NotNull ItemStack stack) {
super(stack);
}

@Override
public void onInteract(@NotNull PlayerInteractEvent event, @NotNull EventPriority priority) {
if (event.getHand() != EquipmentSlot.HAND) {
return;
}

if (event.getAction().isLeftClick()) {
if (TapeMeasureService.isMeasuring(event.getPlayer())) {
TapeMeasureService.cancel(event.getPlayer());
return;
}
}

if (event.getAction().isRightClick() && event.getInteractionPoint() != null) {
if (TapeMeasureService.isMeasuring(event.getPlayer())) {
TapeMeasureService.finish(event.getPlayer());
return;
}

TapeMeasureService.start(event.getPlayer(), event.getInteractionPoint(), this);
}
}

private static class TapeMeasureTask extends BukkitRunnable {
public final Location startLocation;
private Location previousEndLocation;
public final UUID playerId;
public final UUID lineId;
public final UUID textId;
public final int tickInterval;
public final double thickness;
public final Material finishedMaterial;

public TapeMeasureTask(
Location startLocation,
UUID playerId,
UUID lineId,
UUID textId,
int tickInterval,
double thickness,
Material finishedMaterial
) {
super();
this.startLocation = startLocation;
this.playerId = playerId;
this.lineId = lineId;
this.textId = textId;
this.tickInterval = tickInterval;
this.thickness = thickness;
this.finishedMaterial = finishedMaterial;
}

@Override
public void run() {
ItemDisplay line = (ItemDisplay) Bukkit.getEntity(lineId);
TextDisplay text = (TextDisplay) Bukkit.getEntity(textId);
if (line == null || text == null) {
if (line != null) {
line.remove();
}
if (text != null) {
text.remove();
}
cancel();
return;
}

Player player = Bukkit.getPlayer(playerId);
if (player == null) {
line.remove();
cancel();
return;
}

Location endLocation = getPlayerTarget(player);
if (endLocation == null) {
return;
}

if (previousEndLocation == null || previousEndLocation.distance(endLocation) > 0.01) {
int animationTime = tickInterval;
if (line.getLocation().distance(endLocation) > 16) {
// Teleport line closer to ensure it doesn't get so far away that it stops being rendered
// or despawns
line.teleport(endLocation);
animationTime = 0;
}

text.setTeleportDuration(tickInterval);
Vector3d eyeAdjustment = PylonUtils.getDirection(endLocation, player.getEyeLocation())
.normalize()
.mul(0.3);
text.teleport(endLocation.clone().add(Vector.fromJOML(eyeAdjustment)));
text.text(UnitFormat.BLOCKS.format(startLocation.clone().distance(endLocation))
.decimalPlaces(2)
.asComponent()
);

PylonUtils.animate(line, animationTime, new LineBuilder()
.from(startLocation.clone().subtract(line.getLocation()).toVector().toVector3d())
.to(endLocation.clone().subtract(line.getLocation()).toVector().toVector3d())
.thickness(thickness)
.extraLength(thickness)
.build()
.buildForItemDisplay()
);

previousEndLocation = endLocation;
}
}

private static @Nullable Location getPlayerTarget(@NonNull Player player) {
AttributeInstance blockInteractionRange = player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE);
double reach = 4.5;
if (blockInteractionRange != null) {
reach = blockInteractionRange.getValue();
}

RayTraceResult result = player.rayTraceBlocks(reach);
if (result != null && result.getHitBlock() != null) {
Vector vector = result.getHitPosition();
return new Location(result.getHitBlock().getWorld(), vector.getX(), vector.getY(), vector.getZ());
}

return null;
}
}

private static final class TapeMeasureService {

private static final Map<UUID, TapeMeasureTask> tasks = new HashMap<>();

public static boolean isMeasuring(@NotNull Player player) {
return tasks.containsKey(player.getUniqueId());
}

public static void start(@NotNull Player player, @NotNull Location startLocation, @NotNull TapeMeasure tapeMeasure) {
if (isMeasuring(player)) {
cancel(player);
}

ItemDisplay line = new ItemDisplayBuilder()
.material(tapeMeasure.material)
.persistent(false)
.transformation(new TransformBuilder().scale(0))
.build(startLocation);

TextDisplay text = new TextDisplayBuilder()
.persistent(false)
.transformation(new TransformBuilder()
.translate(0, 0.2, 0)
.scale(0.5)
)
.backgroundColor(Color.fromARGB(0, 0, 0, 0))
.billboard(Display.Billboard.VERTICAL)
.build(startLocation);

TapeMeasureTask task = new TapeMeasureTask(
startLocation,
player.getUniqueId(),
line.getUniqueId(),
text.getUniqueId(),
tapeMeasure.tickInterval,
tapeMeasure.thickness,
tapeMeasure.finishedMaterial
);
task.runTaskTimer(Pylon.getInstance(), 0, tapeMeasure.tickInterval);
tasks.put(player.getUniqueId(), task);
}

private static void finish(@NotNull Player player) {
TapeMeasureTask task = tasks.get(player.getUniqueId());
task.cancel();

if (task.lineId != null) {
if (Bukkit.getEntity(task.lineId) instanceof ItemDisplay line) {
line.setItemStack(new ItemStack(task.finishedMaterial));
}
}
}

private static void cancel(@NotNull Player player) {
TapeMeasureTask task = tasks.remove(player.getUniqueId());
if (task == null) {
return;
}

task.cancel();

if (task.lineId != null) {
Entity line = Bukkit.getEntity(task.lineId);
if (line != null) {
line.remove();
}
}

if (task.textId != null) {
Entity text = Bukkit.getEntity(task.textId);
if (text != null) {
text.remove();
}
}
}
}

public static class TapeMeasureListener implements Listener {

@EventHandler
private static void onPlayerScroll(@NonNull PlayerItemHeldEvent event) {
ItemStack heldItem = event.getPlayer().getInventory().getItem(event.getPreviousSlot());
if (isRebarItem(heldItem, TapeMeasure.class)) {
TapeMeasureService.cancel(event.getPlayer());
}
}

@EventHandler
private static void onSwap(@NonNull PlayerSwapHandItemsEvent event) {
if (isRebarItem(event.getMainHandItem(), TapeMeasureService.class)) {
TapeMeasureService.cancel(event.getPlayer());
}
Comment thread
LordIdra marked this conversation as resolved.
Outdated

if (isRebarItem(event.getOffHandItem(), TapeMeasure.class)) {
TapeMeasureService.cancel(event.getPlayer());
}
}

@EventHandler
private static void onInventoryChange(@NonNull PlayerInventorySlotChangeEvent event) {
if (event.getSlot() == event.getPlayer().getInventory().getHeldItemSlot()
&& isRebarItem(event.getOldItemStack(), TapeMeasure.class)
&& !isRebarItem(event.getNewItemStack(), TapeMeasure.class)) {
TapeMeasureService.cancel(event.getPlayer());
}
}

@EventHandler
private static void onDrop(@NonNull PlayerDropItemEvent event) {
if (isRebarItem(event.getItemDrop().getItemStack(), TapeMeasure.class)) {
TapeMeasureService.cancel(event.getPlayer());
}
}
}
}
8 changes: 8 additions & 0 deletions src/main/resources/lang/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2546,6 +2546,14 @@ item:
<diamond> 3x <yellow>Polished Deepslate Wall</yellow>
waila: "Diesel Experience Bottler | %progress%"

tape_measure:
name: "Tape Measure"
lore: |-
<arrow> Measures the distance between two points
<arrow> <insn>Right click</insn> to start measuring
<arrow> <insn>Right click</insn> again to set the end point
<arrow> <insn>Left click</insn> to clear

fluid:
tag:
melting-point: "Melting point: %temperature%"
Expand Down
13 changes: 12 additions & 1 deletion src/main/resources/recipes/minecraft/crafting_shaped.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2952,4 +2952,15 @@ pylon:experience_drain:
D: pylon:shimmer_dust_1
P: minecraft:ender_pearl
result: pylon:experience_drain
category: building
category: building

pylon:tape_measure:
pattern:
- " SS"
- "SNS"
- " S "
key:
S: minecraft:string
N: minecraft:iron_nugget
result: pylon:tape_measure
category: equipment
8 changes: 7 additions & 1 deletion src/main/resources/researches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ baking:
- pylon:monster_jerky

showing_off:
item: stone_brick_wall
item: pylon:pedestal
cost: 2
unlocks:
- pylon:pedestal
- pylon:confetti_popper

tape_measure:
item: pylon:tape_measure
cost: 3
unlocks:
- pylon:tape_measure

talismans_1:
item: pylon:health_talisman_simple
cost: 10
Expand Down
Loading
Loading