diff --git a/eco-api/src/main/java/com/willfp/eco/core/price/Price.java b/eco-api/src/main/java/com/willfp/eco/core/price/Price.java new file mode 100644 index 00000000..534576d7 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/price/Price.java @@ -0,0 +1,26 @@ +package com.willfp.eco.core.price; + +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +/** + * A price that a player should pay. + */ +public interface Price { + /** + * Get if the player can afford the price. + * + * @param player The player. + * @return If the player can afford. + */ + boolean canAfford(@NotNull Player player); + + /** + * Make the player pay the price. + *
+ * Only run this if the player can afford the price. + * + * @param player The player. + */ + void pay(@NotNull Player player); +} diff --git a/eco-api/src/main/java/com/willfp/eco/core/price/PriceFactory.java b/eco-api/src/main/java/com/willfp/eco/core/price/PriceFactory.java new file mode 100644 index 00000000..1883f0d1 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/price/PriceFactory.java @@ -0,0 +1,27 @@ +package com.willfp.eco.core.price; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Create prices. + */ +public interface PriceFactory { + /** + * Get the names (how the price looks in lookup strings). + *
+ * For example, for XP Levels this would be 'l', 'xpl', 'levels', etc.
+ *
+ * @return The allowed names.
+ */
+ @NotNull List
+ * A price string should look like {@code 5000}, {@code 2000 levels},
+ * {@code 200 g_souls}, {@code 200 pots of gold}, etc.
+ *
+ * @param key The key.
+ * @return The price, or {@link PriceFree} if invalid.
+ */
+ @NotNull
+ public static Price lookup(@NotNull final String key) {
+ String[] split = key.split(" ");
+
+ if (split.length == 0) {
+ return new PriceFree();
+ }
+
+ double value;
+
+ try {
+ value = Double.parseDouble(split[0]);
+ } catch (NumberFormatException e) {
+ value = 0.0;
+ }
+
+ if (split.length == 1) {
+ return new PriceEconomy(value);
+ }
+
+ String name = String.join(" ", Arrays.copyOfRange(split, 1, split.length));
+
+ PriceFactory factory = FACTORIES.get(name.toLowerCase());
+
+ if (factory == null) {
+ TestableItem item = Items.lookup(name.toLowerCase());
+
+ if (item instanceof EmptyTestableItem) {
+ return new PriceFree();
+ }
+
+ return new PriceItem((int) Math.round(value), item);
+ }
+
+ return factory.create(value);
+ }
+
+ private Prices() {
+ throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+ }
+}
diff --git a/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceEconomy.java b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceEconomy.java
new file mode 100644
index 00000000..eacc2fda
--- /dev/null
+++ b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceEconomy.java
@@ -0,0 +1,35 @@
+package com.willfp.eco.core.price.impl;
+
+import com.willfp.eco.core.integrations.economy.EconomyManager;
+import com.willfp.eco.core.price.Price;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Economy-based price (for Vault, Treasury, etc.)
+ */
+public final class PriceEconomy implements Price {
+ /**
+ * The value of the price.
+ */
+ private final double value;
+
+ /**
+ * Create a new economy-based price.
+ *
+ * @param value The value.
+ */
+ public PriceEconomy(final double value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean canAfford(@NotNull Player player) {
+ return EconomyManager.getBalance(player) >= value;
+ }
+
+ @Override
+ public void pay(@NotNull Player player) {
+ EconomyManager.removeMoney(player, value);
+ }
+}
diff --git a/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceFree.java b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceFree.java
new file mode 100644
index 00000000..c9eb95b4
--- /dev/null
+++ b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceFree.java
@@ -0,0 +1,27 @@
+package com.willfp.eco.core.price.impl;
+
+import com.willfp.eco.core.price.Price;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Free (default) price.
+ */
+public final class PriceFree implements Price {
+ /**
+ * Create a new free price.
+ */
+ public PriceFree() {
+
+ }
+
+ @Override
+ public boolean canAfford(@NotNull Player player) {
+ return true;
+ }
+
+ @Override
+ public void pay(@NotNull Player player) {
+ // Do nothing.
+ }
+}
diff --git a/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceItem.java b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceItem.java
new file mode 100644
index 00000000..2adb03e9
--- /dev/null
+++ b/eco-api/src/main/java/com/willfp/eco/core/price/impl/PriceItem.java
@@ -0,0 +1,77 @@
+package com.willfp.eco.core.price.impl;
+
+import com.willfp.eco.core.items.TestableItem;
+import com.willfp.eco.core.price.Price;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Item-based price.
+ */
+public final class PriceItem implements Price {
+ /**
+ * The amount of items.
+ */
+ private final int amountToRemove;
+
+ /**
+ * The item.
+ */
+ private final TestableItem item;
+
+ /**
+ * Create a new economy-based price.
+ *
+ * @param amount The amount.
+ */
+ public PriceItem(final int amount,
+ @NotNull final TestableItem item) {
+ this.amountToRemove = Math.max(0, amount);
+ this.item = item;
+ }
+
+ @Override
+ public boolean canAfford(@NotNull Player player) {
+ if (amountToRemove == 0) {
+ return true;
+ }
+
+ int count = 0;
+
+ for (ItemStack itemStack : player.getInventory().getContents()) {
+ if (item.matches(itemStack)) {
+ count += itemStack.getAmount();
+ }
+ }
+
+ return count >= amountToRemove;
+ }
+
+ @Override
+ public void pay(@NotNull Player player) {
+ int count = 0;
+
+ for (ItemStack itemStack : player.getInventory().getContents()) {
+ if (count >= amountToRemove) {
+ break;
+ }
+
+ if (item.matches(itemStack)) {
+ int itemAmount = itemStack.getAmount();
+
+ if (itemAmount > amountToRemove) {
+ itemStack.setAmount(itemAmount - amountToRemove);
+ }
+
+ if (itemAmount <= amountToRemove) {
+ itemStack.setAmount(0);
+ itemStack.setType(Material.AIR);
+ }
+
+ count += itemAmount;
+ }
+ }
+ }
+}
diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryEconomy.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryEconomy.kt
new file mode 100644
index 00000000..70cb5356
--- /dev/null
+++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryEconomy.kt
@@ -0,0 +1,14 @@
+package com.willfp.eco.internal.price
+
+import com.willfp.eco.core.price.Price
+import com.willfp.eco.core.price.PriceFactory
+import com.willfp.eco.core.price.impl.PriceEconomy
+
+object PriceFactoryEconomy : PriceFactory {
+ override fun getNames() = listOf(
+ "coins",
+ "$"
+ )
+
+ override fun create(value: Double): Price = PriceEconomy(value)
+}
diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXP.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXP.kt
new file mode 100644
index 00000000..5708d8ef
--- /dev/null
+++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXP.kt
@@ -0,0 +1,26 @@
+package com.willfp.eco.internal.price
+
+import com.willfp.eco.core.price.Price
+import com.willfp.eco.core.price.PriceFactory
+import org.bukkit.entity.Player
+import kotlin.math.roundToInt
+
+object PriceFactoryXP : PriceFactory {
+ override fun getNames() = listOf(
+ "xp",
+ "exp",
+ "experience"
+ )
+
+ override fun create(value: Double): Price = PriceXP(value.roundToInt())
+
+ private class PriceXP(
+ private val xp: Int
+ ) : Price {
+ override fun canAfford(player: Player) = player.totalExperience >= xp
+
+ override fun pay(player: Player) {
+ player.totalExperience -= xp
+ }
+ }
+}
diff --git a/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXPLevels.kt b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXPLevels.kt
new file mode 100644
index 00000000..2b04e0ec
--- /dev/null
+++ b/eco-core/core-backend/src/main/kotlin/com/willfp/eco/internal/price/PriceFactoryXPLevels.kt
@@ -0,0 +1,29 @@
+package com.willfp.eco.internal.price
+
+import com.willfp.eco.core.price.Price
+import com.willfp.eco.core.price.PriceFactory
+import org.bukkit.entity.Player
+import kotlin.math.roundToInt
+
+object PriceFactoryXPLevels : PriceFactory {
+ override fun getNames() = listOf(
+ "levels",
+ "xp levels",
+ "exp levels",
+ "l",
+ "xpl",
+ "expl"
+ )
+
+ override fun create(value: Double): Price = PriceXPLevel(value.roundToInt())
+
+ private class PriceXPLevel(
+ private val levels: Int
+ ) : Price {
+ override fun canAfford(player: Player) = player.level >= levels
+
+ override fun pay(player: Player) {
+ player.level -= levels
+ }
+ }
+}
diff --git a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt
index 0bbdacf0..9ad3a077 100644
--- a/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt
+++ b/eco-core/core-plugin/src/main/kotlin/com/willfp/eco/internal/spigot/EcoSpigotPlugin.kt
@@ -17,6 +17,7 @@ import com.willfp.eco.core.integrations.mcmmo.McmmoManager
import com.willfp.eco.core.integrations.placeholder.PlaceholderManager
import com.willfp.eco.core.integrations.shop.ShopManager
import com.willfp.eco.core.items.Items
+import com.willfp.eco.core.price.Prices
import com.willfp.eco.internal.entities.EntityArgParserAdult
import com.willfp.eco.internal.entities.EntityArgParserAttackDamage
import com.willfp.eco.internal.entities.EntityArgParserAttackSpeed
@@ -45,11 +46,14 @@ import com.willfp.eco.internal.items.ArgParserTexture
import com.willfp.eco.internal.items.ArgParserUnbreakable
import com.willfp.eco.internal.lookup.SegmentParserGroup
import com.willfp.eco.internal.lookup.SegmentParserUseIfPresent
+import com.willfp.eco.internal.price.PriceFactoryEconomy
+import com.willfp.eco.internal.price.PriceFactoryXP
+import com.willfp.eco.internal.price.PriceFactoryXPLevels
import com.willfp.eco.internal.spigot.arrows.ArrowDataListener
import com.willfp.eco.internal.spigot.data.DataListener
import com.willfp.eco.internal.spigot.data.DataYml
-import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.PlayerBlockListener
+import com.willfp.eco.internal.spigot.data.ProfileHandler
import com.willfp.eco.internal.spigot.data.storage.ProfileSaver
import com.willfp.eco.internal.spigot.display.PacketAutoRecipe
import com.willfp.eco.internal.spigot.display.PacketChat
@@ -161,6 +165,10 @@ abstract class EcoSpigotPlugin : EcoPlugin() {
Entities.registerArgParser(EntityArgParserSilent)
Entities.registerArgParser(EntityArgParserEquipment)
+ Prices.registerPriceFactory(PriceFactoryEconomy)
+ Prices.registerPriceFactory(PriceFactoryXPLevels)
+ Prices.registerPriceFactory(PriceFactoryXP)
+
CraftingRecipeListener.registerListener(ComplexInComplex)
CraftingRecipeListener.registerListener(ComplexInVanilla)