Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExprDurability/ExprMaxDurability - account for custom durability #6803

Merged
merged 12 commits into from
Jul 1, 2024
Merged
40 changes: 40 additions & 0 deletions src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.util.slot.Slot;
import org.bukkit.Material;
import org.bukkit.TreeType;
import org.bukkit.inventory.ItemStack;
Expand All @@ -35,6 +36,8 @@
public class ItemUtils {

public static final boolean HAS_MAX_DAMAGE = Skript.methodExists(Damageable.class, "getMaxDamage");
// Introduced in Paper 1.21
public static final boolean HAS_RESET = Skript.methodExists(Damageable.class, "resetDamage");

/**
* Gets damage/durability of an item, or 0 if it does not have damage.
Expand All @@ -60,6 +63,25 @@ public static int getMaxDamage(ItemStack itemStack) {
return itemStack.getType().getMaxDurability();
}

/**
* Set the max damage/durability of an item
*
* @param itemStack ItemStack to set max damage
* @param maxDamage Amount of new max damage
*/
public static void setMaxDamage(ItemStack itemStack, int maxDamage) {
ItemMeta meta = itemStack.getItemMeta();
if (HAS_MAX_DAMAGE && meta instanceof Damageable) {
Damageable damageable = (Damageable) meta;
if (HAS_RESET) {
//damageable.resetDamage(); TODO (waiting on Skript to update to Paper 1.21)
} else {
damageable.setMaxDamage(Math.max(1, maxDamage));
}
itemStack.setItemMeta(damageable);
}
}

/**
* Sets damage/durability of an item if possible.
* @param itemStack Item to modify.
Expand Down Expand Up @@ -139,6 +161,24 @@ public static Material asItem(Material type) {
// Assume (naively) that all types are valid items
return type;
}

/**
* Convert an ItemType/Slot to ItemStack
* Will also accept an ItemStack that will return itself
*
* @param object Object to convert
* @return ItemStack from slot/itemtype
*/
@Nullable
public static ItemStack asItemStack(Object object) {
if (object instanceof ItemType)
return ((ItemType) object).getRandom();
else if (object instanceof Slot)
return ((Slot) object).getItem();
else if (object instanceof ItemStack)
return ((ItemStack) object);
return null;
}

/**
* Tests whether two item stacks are of the same type, i.e. it ignores the amounts.
Expand Down
71 changes: 28 additions & 43 deletions src/main/java/ch/njol/skript/expressions/ExprDurability.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,22 @@
*/
package ch.njol.skript.expressions;

import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.util.Kleenean;
import org.bukkit.Material;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.bukkitutil.ItemUtils;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.util.slot.Slot;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;

@Name("Damage Value/Durability")
@Description("The damage value/durability of an item.")
Expand All @@ -51,7 +48,7 @@ public class ExprDurability extends SimplePropertyExpression<Object, Integer> {
private boolean durability;

static {
register(ExprDurability.class, Integer.class, "(damage[s] [value[s]]|1:durabilit(y|ies))", "itemtypes/slots");
register(ExprDurability.class, Integer.class, "(damage[s] [value[s]]|1:durabilit(y|ies))", "itemtypes/itemstacks/slots");
}

@Override
Expand All @@ -63,11 +60,11 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
@Override
@Nullable
public Integer convert(Object object) {
ItemType itemType = asItemType(object);
if (itemType == null)
ItemStack itemStack = ItemUtils.asItemStack(object);
if (itemStack == null)
return null;
ItemMeta meta = itemType.getItemMeta();
return meta instanceof Damageable ? convertToDamage(itemType.getMaterial(), ((Damageable) meta).getDamage()) : 0;
int damage = ItemUtils.getDamage(itemStack);
return convertToDamage(itemStack, damage);
}

@Override
Expand All @@ -90,40 +87,38 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
if (mode == ChangeMode.REMOVE)
change = -change;
for (Object object : getExpr().getArray(event)) {
ItemType itemType = asItemType(object);
if (itemType == null)
continue;

ItemMeta meta = itemType.getItemMeta();
if (!(meta instanceof Damageable))
ItemStack itemStack = ItemUtils.asItemStack(object);
if (itemStack == null)
continue;
Damageable damageable = (Damageable) meta;

Material material = itemType.getMaterial();
int newAmount;
switch (mode) {
case ADD:
case REMOVE:
int current = convertToDamage(material, damageable.getDamage());
damageable.setDamage(convertToDamage(material, current + change));
int current = convertToDamage(itemStack, ItemUtils.getDamage(itemStack));
newAmount = current + change;
break;
case SET:
damageable.setDamage(convertToDamage(material, change));
newAmount = change;
break;
case DELETE:
case RESET:
damageable.setDamage(0);
default:
newAmount = 0;
}

itemType.setItemMeta(meta);
ItemUtils.setDamage(itemStack, convertToDamage(itemStack, newAmount));
if (object instanceof Slot)
((Slot) object).setItem(itemType.getRandom());
((Slot) object).setItem(itemStack);
else if (object instanceof ItemType)
((ItemType) object).setItemMeta(itemStack.getItemMeta());
}
}

private int convertToDamage(Material material, int value) {
private int convertToDamage(ItemStack itemStack, int value) {
if (!durability)
return value;
int maxDurability = material.getMaxDurability();

int maxDurability = ItemUtils.getMaxDamage(itemStack);

if (maxDurability == 0)
return 0;
return maxDurability - value;
Expand All @@ -139,14 +134,4 @@ public String getPropertyName() {
return durability ? "durability" : "damage";
}

@Nullable
private static ItemType asItemType(Object object) {
if (object instanceof ItemType)
return (ItemType) object;
ItemStack itemStack = ((Slot) object).getItem();
if (itemStack == null)
return null;
return new ItemType(itemStack);
}

}
101 changes: 81 additions & 20 deletions src/main/java/ch/njol/skript/expressions/ExprMaxDurability.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,107 @@
*/
package ch.njol.skript.expressions;

import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.Nullable;

import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.bukkitutil.ItemUtils;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.skript.util.slot.Slot;
import ch.njol.util.coll.CollectionUtils;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;

@Name("Max Durability")
@Description("The maximum durability of an item.")
@Examples({"maximum durability of diamond sword",
"if max durability of player's tool is not 0: # Item is damageable"})
@Since("2.5")
public class ExprMaxDurability extends SimplePropertyExpression<Object, Long> {
@Description({"The maximum durability of an item. Changing requires Minecraft 1.20.5+",
"Note: 'delete' will remove the max durability from the item (making it a non-damageable item). Delete requires Paper 1.21+"})
@Examples({
"maximum durability of diamond sword",
"if max durability of player's tool is not 0: # Item is damageable",
"set max durability of player's tool to 5000",
"add 5 to max durability of player's tool",
"reset max durability of player's tool",
"delete max durability of player's tool"
})
@Since("2.5, INSERT VERSION (change)")
ShaneBeee marked this conversation as resolved.
Show resolved Hide resolved
public class ExprMaxDurability extends SimplePropertyExpression<Object, Integer> {

static {
register(ExprMaxDurability.class, Long.class, "max[imum] durabilit(y|ies)", "itemstacks/slots");
register(ExprMaxDurability.class, Integer.class, "max[imum] (durabilit(y|ies)|damage)", "itemtypes/itemstacks/slots");
}

@Override
@Nullable
public Long convert(Object o) {
if (o instanceof Slot) {
final ItemStack i = ((Slot) o).getItem();
return i == null ? null : (long) i.getType().getMaxDurability();
} else {
return (long) ((ItemStack) o).getType().getMaxDurability();
public Integer convert(Object object) {
ItemStack itemStack = ItemUtils.asItemStack(object);
if (itemStack == null)
return null;
return ItemUtils.getMaxDamage(itemStack);
}

@Override
public @Nullable Class<?>[] acceptChange(ChangeMode mode) {
if (ItemUtils.HAS_MAX_DAMAGE) {
switch (mode) {
case SET:
case ADD:
case REMOVE:
case RESET:
return CollectionUtils.array(Number.class);
case DELETE:
if (ItemUtils.HAS_RESET)
return CollectionUtils.array();
}
}
return null;
}

@Override
public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
int change = delta == null ? 0 : ((Number) delta[0]).intValue();
if (mode == ChangeMode.REMOVE)
change = -change;

for (Object object : getExpr().getArray(event)) {
ItemStack itemStack = ItemUtils.asItemStack(object);
if (itemStack == null)
continue;

int newValue;
switch (mode) {
case ADD:
case REMOVE:
newValue = ItemUtils.getMaxDamage(itemStack) + change;
break;
case SET:
newValue = change;
break;
case DELETE:
newValue = 0;
break;
default:
newValue = itemStack.getType().getMaxDurability();
}

ItemUtils.setMaxDamage(itemStack, newValue);
if (object instanceof Slot)
((Slot) object).setItem(itemStack);
if (object instanceof ItemType)
((ItemType) object).setItemMeta(itemStack.getItemMeta());
}
}

@Override
public Class<? extends Long> getReturnType() {
return Long.class;
public Class<? extends Integer> getReturnType() {
return Integer.class;
}

@Override
protected String getPropertyName() {
return "max durability";
}

}
4 changes: 3 additions & 1 deletion src/main/java/ch/njol/skript/structures/StructCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,9 @@ private void scheduleCommandSync() {
if (SYNC_COMMANDS.get()) {
SYNC_COMMANDS.set(false);
if (DELAY_COMMAND_SYNCING) {
Bukkit.getScheduler().runTask(Skript.getInstance(), this::forceCommandSync);
// if the plugin is disabled, the server is likely closing and delaying will cause an error.
if (Bukkit.getPluginManager().isPluginEnabled(Skript.getInstance()))
Bukkit.getScheduler().runTask(Skript.getInstance(), this::forceCommandSync);
} else {
forceCommandSync();
}
Expand Down
6 changes: 6 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprDurability.sk
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ test "durability":
set durability of {_i} to 0
assert damage of {_i} is {_max} with "max item damage failed"
assert durability of {_i} is 0 with "zero item durability failed"

test "durability - custom" when running minecraft "1.20.5":
set {_i} to 1 of iron sword
set max durability of {_i} to 1000
damage {_i} by 1
assert durability of {_i} = 999 with "durability of iron sword should be 999"
10 changes: 10 additions & 0 deletions src/test/skript/tests/syntaxes/expressions/ExprMaxDurability.sk
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ test "max durability":
assert max durability of glass is 0 with "max durability of glass failed"
assert max durability of shears is 238 with "max durability of shears failed"
assert max durability of diamond sword is 1561 with "max durability of diamond sword failed"

test "max durability - custom" when running minecraft "1.20.5":
set {_i} to 1 of iron sword
assert max durability of {_i} = 250 with "max durability of iron sword should be 250"

set max durability of {_i} to 1000
assert max durability of {_i} = 1000 with "adjusted max durability of iron sword should be 1000"

reset max durability of {_i}
assert max durability of {_i} = 250 with "reset max durability of iron sword should be 250"