added different types of the builder wand

with varying durability, max blocks and available modes
also fixed anvil bug (can't enchant an item with an enchantment of higher level but the same type of an enchantment on the tool
This commit is contained in:
XTerPL 2025-02-01 19:12:53 +01:00
parent 453e3f84ff
commit 4ffcf63689
18 changed files with 236 additions and 82 deletions

View file

@ -19,6 +19,9 @@ import de.blazemcworld.blazinggames.BlazingGames;
import de.blazemcworld.blazinggames.items.ContextlessItem;
import de.blazemcworld.blazinggames.items.CustomItem;
import de.blazemcworld.blazinggames.items.change.ItemChangeProviders;
import de.blazemcworld.blazinggames.items.predicates.ItemPredicate;
import de.blazemcworld.blazinggames.items.predicates.MaterialItemPredicate;
import io.papermc.paper.datacomponent.DataComponentTypes;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
@ -41,9 +44,33 @@ import java.util.Map;
public class BuilderWand extends ContextlessItem {
private static final NamespacedKey modeKey = BlazingGames.get().key("builder_mode");
private final NamespacedKey key;
private final Component name;
private final int maxBlocks;
private final int durability;
private final Material gem;
private final Material handle;
private final ItemPredicate repairPredicate;
private final List<BuilderWandMode> availableModes;
public BuilderWand(NamespacedKey key, Component name, int maxBlocks, int durability, Material gem, Material handle, ItemPredicate repairPredicate, BuilderWandMode... availableModes) {
this.key = key;
this.name = name;
this.maxBlocks = maxBlocks;
this.durability = durability;
this.gem = gem;
this.handle = handle;
this.repairPredicate = repairPredicate;
this.availableModes = List.of(availableModes);
}
public BuilderWand(NamespacedKey key, Component name, int maxBlocks, int durability, Material gem, Material handle, BuilderWandMode... availableModes) {
this(key, name, maxBlocks, durability, gem, handle, new MaterialItemPredicate(gem), availableModes);
}
@Override
public @NotNull NamespacedKey getKey() {
return BlazingGames.get().key("builder_wand");
return key;
}
@Override
@ -51,6 +78,11 @@ public class BuilderWand extends ContextlessItem {
return 1;
}
@Override
protected ItemPredicate repairPredicate() {
return repairPredicate;
}
@Override
protected @NotNull ItemStack modifyMaterial(ItemStack wand) {
ItemMeta meta = wand.getItemMeta();
@ -59,12 +91,15 @@ public class BuilderWand extends ContextlessItem {
wand.setItemMeta(meta);
wand.setData(DataComponentTypes.DAMAGE, 0);
wand.setData(DataComponentTypes.MAX_DAMAGE, durability);
return wand;
}
@Override
protected @NotNull Component itemName() {
return Component.text("Builder's Wand").color(NamedTextColor.GOLD);
return name;
}
@Override
@ -73,7 +108,11 @@ public class BuilderWand extends ContextlessItem {
return List.of();
}
return List.of(Component.text(getModeText(wand)).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false));
return List.of(
Component.text("Can place up to " + maxBlocks + " blocks")
.color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false),
Component.text(getModeText(wand)).color(NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false)
);
}
public ItemStack cycleMode(ItemStack wand) {
@ -86,7 +125,11 @@ public class BuilderWand extends ContextlessItem {
ItemMeta meta = wand.getItemMeta();
BuilderWandMode mode = meta.getPersistentDataContainer().getOrDefault(modeKey, BuilderWandMode.persistentType, BuilderWandMode.NO_LOCK);
mode = mode.getNextMode();
int idx = availableModes.indexOf(mode);
idx++;
if(idx >= availableModes.size()) idx = 0;
mode = availableModes.get(idx);
meta.getPersistentDataContainer().set(modeKey, BuilderWandMode.persistentType, mode);
@ -125,19 +168,35 @@ public class BuilderWand extends ContextlessItem {
Inventory inv = player.getInventory();
for (ItemStack itemStack : inv.getStorageContents()) {
if(itemStack == null) continue;
if(itemStack.getType() != itemMaterial) continue;
if(CustomItem.isCustomItem(itemStack)) continue;
if(player.getGameMode() == GameMode.CREATIVE) {
maxBlocks = this.maxBlocks;
}
else {
for (ItemStack itemStack : inv.getStorageContents()) {
if(itemStack == null) continue;
if(itemStack.getType() != itemMaterial) continue;
if(CustomItem.isCustomItem(itemStack)) continue;
maxBlocks += itemStack.getAmount();
maxBlocks += itemStack.getAmount();
}
if(eventItem.hasData(DataComponentTypes.DAMAGE) &&
eventItem.hasData(DataComponentTypes.MAX_DAMAGE) &&
!eventItem.hasData(DataComponentTypes.UNBREAKABLE)) {
int damage = eventItem.getDataOrDefault(DataComponentTypes.DAMAGE, 0);
int maxDamage = eventItem.getDataOrDefault(DataComponentTypes.MAX_DAMAGE, 0);
int durability = maxDamage - damage;
if(maxBlocks > durability) {
maxBlocks = durability;
}
}
if(maxBlocks > this.maxBlocks) {
maxBlocks = this.maxBlocks;
}
}
if(maxBlocks > 128) {
maxBlocks = 128;
}
if(maxBlocks <= 0 && player.getGameMode() != GameMode.CREATIVE)
if(maxBlocks <= 0)
{
return 0;
}
@ -241,8 +300,8 @@ public class BuilderWand extends ContextlessItem {
" R ",
"R "
);
wandRecipe.setIngredient('R', Material.BLAZE_ROD);
wandRecipe.setIngredient('S', Material.NETHER_STAR);
wandRecipe.setIngredient('R', handle);
wandRecipe.setIngredient('S', gem);
return Map.of(
getKey(), wandRecipe

View file

@ -124,16 +124,4 @@ public enum BuilderWandMode {
public String getModeText() {
return modeText;
}
public BuilderWandMode getNextMode() {
return switch(this) {
case NO_LOCK -> HORIZONTAL;
case HORIZONTAL -> VERTICAL;
case VERTICAL -> NORTH_SOUTH;
case NORTH_SOUTH -> NORTH_SOUTH_VERTICAL;
case NORTH_SOUTH_VERTICAL -> EAST_WEST;
case EAST_WEST -> EAST_WEST_VERTICAL;
case EAST_WEST_VERTICAL -> NO_LOCK;
};
}
}

View file

@ -87,6 +87,8 @@ public class VanillaEnchantmentWrapper implements EnchantmentWrapper {
@Override
public boolean conflictsWith(Enchantment other) {
if(other == enchantment) return false;
return enchantment.conflictsWith(other) || other.conflictsWith(enchantment);
}

View file

@ -16,6 +16,7 @@
package de.blazemcworld.blazinggames.events;
import de.blazemcworld.blazinggames.BlazingGames;
import de.blazemcworld.blazinggames.builderwand.BuilderWand;
import de.blazemcworld.blazinggames.crates.CrateData;
import de.blazemcworld.blazinggames.crates.CrateManager;
import de.blazemcworld.blazinggames.crates.DeathCrateKey;
@ -253,10 +254,11 @@ public class InteractEventListener implements Listener {
}
}
}
if(CustomItems.BUILDER_WAND.matchItem(eventItem)) {
if(CustomItem.getCustomItem(eventItem) instanceof BuilderWand wand) {
if(!player.hasCooldown(eventItem)) {
int blocksUsed = CustomItems.BUILDER_WAND.build(player, eventItem, block, face, clampedInteractionPoint);
int blocksUsed = wand.build(player, eventItem, block, face, clampedInteractionPoint);
if(blocksUsed > 0) {
player.damageItemStack(event.getHand(), blocksUsed);
player.setCooldown(eventItem, 5);
player.getWorld().playSound(player, Sound.ENTITY_CHICKEN_STEP, 1, 1.25f);
}
@ -267,14 +269,14 @@ public class InteractEventListener implements Listener {
}
if(event.getAction().isLeftClick() && hand != null) {
if(player.isSneaking() && CustomItems.BUILDER_WAND.matchItem(eventItem)) {
if(player.isSneaking() && CustomItem.getCustomItem(eventItem) instanceof BuilderWand wand) {
event.setCancelled(true);
eventItem = CustomItems.BUILDER_WAND.cycleMode(eventItem);
eventItem = wand.cycleMode(eventItem);
player.getInventory().setItem(hand, eventItem);
player.sendActionBar(Component.text(CustomItems.BUILDER_WAND.getModeText(eventItem)));
player.sendActionBar(Component.text(wand.getModeText(eventItem)));
}
}

View file

@ -17,6 +17,7 @@ package de.blazemcworld.blazinggames.events;
import de.blazemcworld.blazinggames.enchantments.sys.EnchantmentHelper;
import de.blazemcworld.blazinggames.items.CustomItem;
import de.blazemcworld.blazinggames.utils.ItemUtils;
import de.blazemcworld.blazinggames.utils.Pair;
import de.blazemcworld.blazinggames.utils.TextUtils;
import io.papermc.paper.datacomponent.DataComponentTypes;
@ -53,7 +54,35 @@ public class PrepareAnvilEventListener implements Listener {
if(enchantingItem != null && !enchantingItem.isEmpty()) {
repairCost += enchantingItem.getDataOrDefault(DataComponentTypes.REPAIR_COST, 0);
if(CustomItem.isCustomItem(enchantingItem))
if(ItemUtils.canRepairTool(result, enchantingItem)) {
if(result.hasData(DataComponentTypes.MAX_DAMAGE) &&
result.hasData(DataComponentTypes.DAMAGE) &&
!result.hasData(DataComponentTypes.UNBREAKABLE))
{
int damage = result.getData(DataComponentTypes.DAMAGE);
int maxDamage = result.getData(DataComponentTypes.MAX_DAMAGE);
int damageReduced = maxDamage / 4;
int count = 0;
while(count * damageReduced < damage) {
if(count >= enchantingItem.getAmount()) {
break;
}
count++;
}
damage -= count * damageReduced;
if(damage < 0) damage = 0;
result.setData(DataComponentTypes.DAMAGE, damage);
event.getView().setRepairItemCountCost(count);
repairCost += count;
increaseRepairCost = true;
}
}
else if(CustomItem.isCustomItem(enchantingItem))
{
if(EnchantmentHelper.canEnchantItem(result) && result.getAmount() == 1) {
CustomItem<?> item = CustomItem.getCustomItem(enchantingItem);
@ -68,55 +97,22 @@ public class PrepareAnvilEventListener implements Listener {
}
}
}
else
{
if(result.isRepairableBy(enchantingItem)) {
if(result.hasData(DataComponentTypes.MAX_DAMAGE) &&
result.hasData(DataComponentTypes.DAMAGE) &&
!result.hasData(DataComponentTypes.UNBREAKABLE))
{
int damage = result.getData(DataComponentTypes.DAMAGE);
int maxDamage = result.getData(DataComponentTypes.MAX_DAMAGE);
else if(EnchantmentHelper.canEnchantItem(result) && result.getAmount() == 1) {
if(enchantingItem.getType() == Material.ENCHANTED_BOOK) {
Pair<ItemStack, Integer> enchantResult = EnchantmentHelper.enchantFromItem(result, enchantingItem);
result = enchantResult.left;
repairCost += enchantResult.right;
increaseRepairCost = true;
int damageReduced = maxDamage / 4;
int count = 0;
while(count * damageReduced < damage) {
if(count >= enchantingItem.getAmount()) {
break;
}
count++;
}
damage -= count * damageReduced;
if(damage < 0) damage = 0;
result.setData(DataComponentTypes.DAMAGE, damage);
event.getView().setRepairItemCountCost(count);
repairCost += count;
increaseRepairCost = true;
}
minRepairCost = Math.min(minRepairCost, enchantingItem.getDataOrDefault(DataComponentTypes.REPAIR_COST, 0));
}
else {
if(EnchantmentHelper.canEnchantItem(result) && result.getAmount() == 1) {
if(enchantingItem.getType() == Material.ENCHANTED_BOOK) {
Pair<ItemStack, Integer> enchantResult = EnchantmentHelper.enchantFromItem(result, enchantingItem);
result = enchantResult.left;
repairCost += enchantResult.right;
increaseRepairCost = true;
else if(!CustomItem.isCustomItem(result) && enchantingItem.getType() == result.getType()) {
Pair<ItemStack, Integer> enchantResult = EnchantmentHelper.enchantFromItem(result, enchantingItem);
result = enchantResult.left;
repairCost += repairByCombination(result, enchantingItem, 12) + enchantResult.right;
increaseRepairCost = true;
minRepairCost = Math.min(minRepairCost, enchantingItem.getDataOrDefault(DataComponentTypes.REPAIR_COST, 0));
}
else if(!CustomItem.isCustomItem(result) && enchantingItem.getType() == result.getType()) {
Pair<ItemStack, Integer> enchantResult = EnchantmentHelper.enchantFromItem(result, enchantingItem);
result = enchantResult.left;
repairCost += repairByCombination(result, enchantingItem, 12) + enchantResult.right;
increaseRepairCost = true;
minRepairCost = Math.min(minRepairCost, enchantingItem.getDataOrDefault(DataComponentTypes.REPAIR_COST, 0));
}
}
minRepairCost = Math.min(minRepairCost, enchantingItem.getDataOrDefault(DataComponentTypes.REPAIR_COST, 0));
}
}
}

View file

@ -118,6 +118,20 @@ public abstract class CustomItem<T extends ItemContext> implements RecipeProvide
return itemName();
}
public final boolean repairableBy(ItemStack stack) {
ItemPredicate repairPredicate = repairPredicate();
if(repairPredicate == null) {
return false;
}
return repairPredicate.matchItem(stack);
}
protected ItemPredicate repairPredicate() {
return null;
}
// DO NOT CALL THIS METHOD, instead call create() on the item's instance
// also there's no need to set the "custom_item" item tag because
// the create() method does it anyway

View file

@ -20,6 +20,7 @@ import com.google.gson.JsonObject;
import de.blazemcworld.blazinggames.BlazingGames;
import de.blazemcworld.blazinggames.builderwand.BuilderWand;
import de.blazemcworld.blazinggames.builderwand.BuilderWandMode;
import de.blazemcworld.blazinggames.crates.DeathCrateKey;
import de.blazemcworld.blazinggames.crates.SkeletonKey;
import de.blazemcworld.blazinggames.crates.ToGoBoxItem;
@ -29,6 +30,9 @@ import de.blazemcworld.blazinggames.enchantments.sys.EnchantmentWrappers;
import de.blazemcworld.blazinggames.multiblocks.Blueprint;
import de.blazemcworld.blazinggames.packs.HookContext;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import javax.annotation.Nullable;
@ -41,7 +45,6 @@ import java.util.logging.Logger;
public class CustomItems implements ItemProvider {
public static final CustomSlabs CUSTOM_SLABS = new CustomSlabs();
public static final BuilderWand BUILDER_WAND = new BuilderWand();
public static final PortableCraftingTable PORTABLE_CRAFTING_TABLE = new PortableCraftingTable();
public static final TeleportAnchor TELEPORT_ANCHOR = new TeleportAnchor();
public static final Blueprint BLUEPRINT = new Blueprint();
@ -51,6 +54,30 @@ public class CustomItems implements ItemProvider {
public static final ToGoBoxItem TO_GO_BOX = new ToGoBoxItem();
public static final NetherStarChunk NETHER_STAR_CHUNK = new NetherStarChunk();
public static final BuilderWand WOODEN_BUILDER_WAND = new BuilderWand(
BlazingGames.get().key("wooden_builder_wand"),
Component.text("Wooden Builder's Wand"),
16, 256,
Material.FLINT, Material.STICK,
BuilderWandMode.NO_LOCK
);
public static final BuilderWand STORM_BUILDER_WAND = new BuilderWand(
BlazingGames.get().key("storm_builder_wand"),
Component.text("Storm Builder's Wand").color(NamedTextColor.BLUE),
64, 2048,
Material.DIAMOND, Material.BREEZE_ROD,
BuilderWandMode.NO_LOCK, BuilderWandMode.HORIZONTAL, BuilderWandMode.VERTICAL
);
public static final BuilderWand BLAZING_BUILDER_WAND = new BuilderWand(
BlazingGames.get().key("blazing_builder_wand"),
Component.text("Blazing Builder's Wand").color(NamedTextColor.GOLD),
128, 16384,
Material.NETHER_STAR, Material.BLAZE_ROD, CustomItems.NETHER_STAR_CHUNK,
BuilderWandMode.NO_LOCK, BuilderWandMode.HORIZONTAL, BuilderWandMode.VERTICAL,
BuilderWandMode.NORTH_SOUTH, BuilderWandMode.NORTH_SOUTH_VERTICAL,
BuilderWandMode.EAST_WEST, BuilderWandMode.EAST_WEST_VERTICAL
);
public static final EnchantmentTome FUSE_TOME = new EnchantmentTome(BlazingGames.get().key("fuse_tome"), "Fuse Tome", EnchantmentWrappers.MENDING);
public static final EnchantmentTome BIND_TOME = new EnchantmentTome(BlazingGames.get().key("bind_tome"), "Bind Tome", EnchantmentWrappers.BINDING_CURSE);
public static final EnchantmentTome VANISH_TOME = new EnchantmentTome(BlazingGames.get().key("vanish_tome"), "Vanish Tome", EnchantmentWrappers.VANISHING_CURSE);
@ -66,7 +93,6 @@ public class CustomItems implements ItemProvider {
@Override
public Set<CustomItem<?>> getItems() {
return Set.of(
BUILDER_WAND,
PORTABLE_CRAFTING_TABLE,
TELEPORT_ANCHOR,
BLUEPRINT,
@ -75,6 +101,9 @@ public class CustomItems implements ItemProvider {
SKELETON_KEY,
TO_GO_BOX,
NETHER_STAR_CHUNK,
WOODEN_BUILDER_WAND,
STORM_BUILDER_WAND,
BLAZING_BUILDER_WAND,
FUSE_TOME,
BIND_TOME,
VANISH_TOME,
@ -127,6 +156,13 @@ public class CustomItems implements ItemProvider {
BlazingGames.get().log(e);
}
// install animation options
try (InputStream stream = item.getClass().getResourceAsStream("/customitems/" + item.getKey().getKey() + ".png.mcmeta")) {
if (stream != null) context.installTextureAnimationData(item.getKey(), "item", stream.readAllBytes());
} catch (IOException e) {
BlazingGames.get().log(e);
}
// install model
try (InputStream stream = item.getClass().getResourceAsStream("/customitems/" + item.getKey().getKey() + ".json")) {
if (stream != null) context.installModel(item.getKey(), stream.readAllBytes());

View file

@ -54,6 +54,10 @@ public class HookContext {
writeFile("assets/" + namespace.getNamespace() + "/textures/" + type + "/" + namespace.getKey() + ".png", texture);
}
public void installTextureAnimationData(NamespacedKey namespace, String type, byte[] animationData) {
writeFile("assets/" + namespace.getNamespace() + "/textures/" + type + "/" + namespace.getKey() + ".png.mcmeta", animationData);
}
public void installModel(NamespacedKey namespace, byte[] model) {
writeFile("assets/" + namespace.getNamespace() + "/models/" + namespace.getKey() + ".json", model);
}

View file

@ -63,6 +63,13 @@ public class GuiElementsHook implements PackBuildHook {
} catch (IOException e) {
BlazingGames.get().log(e);
}
// install animation options
try (InputStream stream = BlazingGames.class.getResourceAsStream("/gui/" + texture.getKey() + ".png.mcmeta")) {
if (stream != null) context.installTextureAnimationData(texture, "item", stream.readAllBytes());
} catch (IOException e) {
BlazingGames.get().log(e);
}
}
for (NamespacedKey model : getGuiModels()) {

View file

@ -15,6 +15,7 @@
*/
package de.blazemcworld.blazinggames.utils;
import de.blazemcworld.blazinggames.items.CustomItem;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
@ -120,4 +121,16 @@ public class ItemUtils {
default -> mat;
};
}
public static boolean canRepairTool(ItemStack tool, ItemStack sacrificial) {
if(tool == null) return false;
if(sacrificial == null) return false;
CustomItem<?> customItem = CustomItem.getCustomItem(tool);
if(customItem == null) {
return !CustomItem.isCustomItem(sacrificial) && tool.isRepairableBy(sacrificial);
}
return customItem.repairableBy(sacrificial);
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "blazinggames:item/blazing_builder_wand"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,8 @@
{
"animation":
{
"frametime": 2,
"width": 16,
"height": 16
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "blazinggames:item/storm_builder_wand"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

View file

@ -0,0 +1,7 @@
{
"animation":
{
"frametime": 2,
"width": 16
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "blazinggames:item/wooden_builder_wand"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B