diff --git a/paper-api/src/main/java/org/bukkit/inventory/MerchantRecipe.java b/paper-api/src/main/java/org/bukkit/inventory/MerchantRecipe.java
index 1fb4a1c537..620a4df816 100644
--- a/paper-api/src/main/java/org/bukkit/inventory/MerchantRecipe.java
+++ b/paper-api/src/main/java/org/bukkit/inventory/MerchantRecipe.java
@@ -3,7 +3,12 @@ package org.bukkit.inventory;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.List;
+import org.bukkit.Material;
+import org.bukkit.event.entity.VillagerReplenishTradeEvent;
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.util.NumberConversions;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Represents a merchant's trade.
@@ -16,6 +21,30 @@ import org.jetbrains.annotations.NotNull;
* uses to increase.
*
* A trade may or may not reward experience for being completed.
+ *
+ * During trades, the {@link MerchantRecipe} dynamically adjusts the amount of
+ * its first ingredient based on the following criteria:
+ *
1
and the item stack's
+ * {@link ItemStack#getMaxStackSize() maximum stack size}.
*
* @see org.bukkit.event.entity.VillagerReplenishTradeEvent
*/
@@ -26,6 +55,8 @@ public class MerchantRecipe implements Recipe {
private int uses;
private int maxUses;
private boolean experienceReward;
+ private int specialPrice;
+ private int demand;
private int villagerExperience;
private float priceMultiplier;
@@ -34,16 +65,22 @@ public class MerchantRecipe implements Recipe {
}
public MerchantRecipe(@NotNull ItemStack result, int uses, int maxUses, boolean experienceReward) {
- this(result, uses, maxUses, experienceReward, 0, 0.0F);
+ this(result, uses, maxUses, experienceReward, 0, 0.0F, 0, 0);
}
public MerchantRecipe(@NotNull ItemStack result, int uses, int maxUses, boolean experienceReward, int villagerExperience, float priceMultiplier) {
+ this(result, uses, maxUses, experienceReward, villagerExperience, priceMultiplier, 0, 0);
+ }
+
+ public MerchantRecipe(@NotNull ItemStack result, int uses, int maxUses, boolean experienceReward, int villagerExperience, float priceMultiplier, int demand, int specialPrice) {
this.result = result;
this.uses = uses;
this.maxUses = maxUses;
this.experienceReward = experienceReward;
this.villagerExperience = villagerExperience;
this.priceMultiplier = priceMultiplier;
+ this.demand = demand;
+ this.specialPrice = specialPrice;
}
@NotNull
@@ -78,6 +115,95 @@ public class MerchantRecipe implements Recipe {
return copy;
}
+ /**
+ * Gets the {@link #adjust(ItemStack) adjusted} first ingredient.
+ *
+ * @return the adjusted first ingredient, or null
if this
+ * recipe has no ingredients
+ * @see #adjust(ItemStack)
+ */
+ @Nullable
+ public ItemStack getAdjustedIngredient1() {
+ if (this.ingredients.isEmpty()) {
+ return null;
+ }
+
+ ItemStack firstIngredient = this.ingredients.get(0).clone();
+ adjust(firstIngredient);
+ return firstIngredient;
+ }
+
+ /**
+ * Modifies the amount of the given {@link ItemStack} in the same way as
+ * MerchantRecipe dynamically adjusts the amount of the first ingredient
+ * during trading.
+ * 1
and the
+ * {@link ItemStack}'s {@link ItemStack#getMaxStackSize()
+ * maximum stack size}.
+ *
+ * @param itemStack the item to adjust
+ */
+ public void adjust(@Nullable ItemStack itemStack) {
+ if (itemStack == null || itemStack.getType() == Material.AIR || itemStack.getAmount() <= 0) {
+ return;
+ }
+
+ int amount = itemStack.getAmount();
+ int demandAdjustment = Math.max(0, NumberConversions.floor((float) (amount * getDemand()) * getPriceMultiplier()));
+ itemStack.setAmount(Math.max(1, Math.min(itemStack.getMaxStackSize(), amount + demandAdjustment + getSpecialPrice())));
+ }
+
+ /**
+ * Get the value of the demand for the item in {@link #getResult()}.
+ *
+ * @return the demand for the item
+ */
+ public int getDemand() {
+ return demand;
+ }
+
+ /**
+ * Set the value of the demand for the item in {@link #getResult()}.
+ *