refactor: simplify wandering trader offers and add individual spawn chances

This commit is contained in:
besiece committed 2026-11-08 14:32:57 +01:00
1 parent a319d5810b
commit 4c8dc589eb
3 files changed
+40 -15

No files matched your search

@@ -0,0 +1,32 @@
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.wanderingTrader;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import java.util.Objects;
import java.util.function.Supplier;
record TraderOffer(Supplier<ItemStack> resultFactory, int emeraldPrice, int maxUses, double chance) {
TraderOffer {
Objects.requireNonNull(resultFactory, "resultFactory");
if(emeraldPrice < 1 || emeraldPrice > 64)
throw new IllegalArgumentException("emeraldPrice must be between 1 and 64");
if(maxUses < 1)
throw new IllegalArgumentException("maxUses must be positive");
if(!Double.isFinite(chance) || chance < 0 || chance > 1)
throw new IllegalArgumentException("chance must be finite and between 0 and 1");
}
static TraderOffer offer(Material material, int emeraldPrice, int maxUses, double chance) {
Objects.requireNonNull(material, "material");
return new TraderOffer(() -> new ItemStack(material), emeraldPrice, maxUses, chance);
}
MerchantRecipe createRecipe() {
ItemStack result = Objects.requireNonNull(this.resultFactory.get(), "resultFactory result").clone();
MerchantRecipe recipe = new MerchantRecipe(result, 0, this.maxUses, false, 0, 0f);
recipe.addIngredient(new ItemStack(Material.EMERALD, this.emeraldPrice));
return recipe;
}
}
@@ -13,7 +13,7 @@ class WanderingTraderSpawnListener extends ApplianceListener<WanderingTraderTrad
public void onSpawn(CreatureSpawnEvent event) {
if(!(event.getEntity() instanceof WanderingTrader trader)) return;
// Initialize vanilla offers only after the spawn has finished.
// Add custom trades on the next tick, after the spawn has finished.
Bukkit.getScheduler().runTask(Main.instance(), () -> this.getAppliance().addTrades(trader));
}
}
@@ -6,37 +6,30 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.WanderingTrader;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.concurrent.ThreadLocalRandom;
public class WanderingTraderTrades extends Appliance {
private static final int HEART_OF_THE_SEA_PRICE = 32;
private static final int HEART_OF_THE_SEA_MAX_USES = 1;
private final NamespacedKey appliedKey = new NamespacedKey(Main.instance(), "wandering_trader_trades_applied");
private final List<Supplier<MerchantRecipe>> additionalTrades = List.of(WanderingTraderTrades::heartOfTheSeaTrade);
private static MerchantRecipe heartOfTheSeaTrade() {
MerchantRecipe recipe = new MerchantRecipe(
new ItemStack(Material.HEART_OF_THE_SEA), 0, HEART_OF_THE_SEA_MAX_USES, false, 0, 0f
private final List<TraderOffer> additionalTrades = List.of(
TraderOffer.offer(Material.HEART_OF_THE_SEA, 32, 1, 0.5)
);
recipe.addIngredient(new ItemStack(Material.EMERALD, HEART_OF_THE_SEA_PRICE));
return recipe;
}
void addTrades(WanderingTrader trader) {
if(!trader.isValid() || trader.isDead()) return;
if(trader.getPersistentDataContainer().has(this.appliedKey, PersistentDataType.BYTE)) return;
List<MerchantRecipe> recipes = new ArrayList<>(trader.getRecipes());
this.additionalTrades.forEach(factory -> recipes.add(factory.get()));
this.additionalTrades.forEach(offer -> {
if(ThreadLocalRandom.current().nextDouble() < offer.chance()) recipes.add(offer.createRecipe());
});
trader.setRecipes(recipes);
// Mark this trader as having had custom trades applied.
trader.getPersistentDataContainer().set(this.appliedKey, PersistentDataType.BYTE, (byte) 1);
}