diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/trade/TradeOfferHelper.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/trade/TradeOfferHelper.java index ebbfe25258..35572fd9dd 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/trade/TradeOfferHelper.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/trade/TradeOfferHelper.java @@ -18,10 +18,12 @@ import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import org.jetbrains.annotations.ApiStatus; +import net.minecraft.util.Identifier; import net.minecraft.village.TradeOffers; import net.minecraft.village.VillagerProfession; @@ -130,94 +132,104 @@ public interface VillagerOffersAdder { @ApiStatus.Experimental public interface WanderingTraderOffersBuilder { /** - * The pool index for the "buy items" pool. + * The pool ID for the "buy items" pool. * Two trade offers are picked from this pool. * *

In vanilla, this pool contains offers to buy water buckets, baked potatoes, etc. * for emeralds. */ - int BUY_ITEMS_POOL = 0; + Identifier BUY_ITEMS_POOL = new Identifier("minecraft", "buy_items"); /** - * The pool index for the "sell special items" pool. + * The pool ID for the "sell special items" pool. * Two trade offers are picked from this pool. * *

In vanilla, this pool contains offers to sell logs, enchanted iron pickaxes, etc. */ - int SELL_SPECIAL_ITEMS_POOL = 1; + Identifier SELL_SPECIAL_ITEMS_POOL = new Identifier("minecraft", "sell_special_items"); /** - * The pool index for the "sell common items" pool. + * The pool ID for the "sell common items" pool. * Five trade offers are picked from this pool. * *

In vanilla, this pool contains offers to sell flowers, saplings, etc. */ - int SELL_COMMON_ITEMS_POOL = 2; + Identifier SELL_COMMON_ITEMS_POOL = new Identifier("minecraft", "sell_common_items"); /** * Adds a new pool to the offer list. Exactly {@code count} offers are picked from * {@code factories} and offered to customers. + * @param id the ID to be assigned to this pool, to allow further modification * @param count the number of offers to be picked from {@code factories} * @param factories the trade offer factories * @return this builder, for chaining * @throws IllegalArgumentException if {@code count} is not positive or if {@code factories} is empty */ - WanderingTraderOffersBuilder pool(int count, TradeOffers.Factory... factories); + WanderingTraderOffersBuilder pool(Identifier id, int count, TradeOffers.Factory... factories); /** * Adds a new pool to the offer list. Exactly {@code count} offers are picked from * {@code factories} and offered to customers. + * @param id the ID to be assigned to this pool, to allow further modification * @param count the number of offers to be picked from {@code factories} * @param factories the trade offer factories * @return this builder, for chaining * @throws IllegalArgumentException if {@code count} is not positive or if {@code factories} is empty */ - default WanderingTraderOffersBuilder pool(int count, Collection factories) { - return pool(count, factories.toArray(TradeOffers.Factory[]::new)); + default WanderingTraderOffersBuilder pool(Identifier id, int count, Collection factories) { + return pool(id, count, factories.toArray(TradeOffers.Factory[]::new)); } /** * Adds trade offers to the offer list. All offers from {@code factories} are * offered to each customer. + * @param id the ID to be assigned to this pool, to allow further modification * @param factories the trade offer factories * @return this builder, for chaining * @throws IllegalArgumentException if {@code factories} is empty */ - default WanderingTraderOffersBuilder addAll(Collection factories) { - return pool(factories.size(), factories); + default WanderingTraderOffersBuilder addAll(Identifier id, Collection factories) { + return pool(id, factories.size(), factories); } /** * Adds trade offers to the offer list. All offers from {@code factories} are * offered to each customer. + * @param id the ID to be assigned to this pool, to allow further modification * @param factories the trade offer factories * @return this builder, for chaining * @throws IllegalArgumentException if {@code factories} is empty */ - default WanderingTraderOffersBuilder addAll(TradeOffers.Factory... factories) { - return pool(factories.length, factories); + default WanderingTraderOffersBuilder addAll(Identifier id, TradeOffers.Factory... factories) { + return pool(id, factories.length, factories); } /** - * Adds trade offers to an existing pool. + * Adds trade offers to an existing pool identified by an ID. * - *

See the constants for vanilla trade offer pool indices that are always available. - * @param poolIndex the pool index + *

See the constants for vanilla trade offer pool IDs that are always available. + * @param pool the pool ID * @param factories the trade offer factories * @return this builder, for chaining - * @throws IndexOutOfBoundsException if {@code poolIndex} is out of bounds + * @throws IndexOutOfBoundsException if {@code pool} is out of bounds */ - WanderingTraderOffersBuilder addOffersToPool(int poolIndex, TradeOffers.Factory... factories); + WanderingTraderOffersBuilder addOffersToPool(Identifier pool, TradeOffers.Factory... factories); /** - * Adds trade offers to an existing pool. + * Adds trade offers to an existing pool identified by an ID. * - *

See the constants for vanilla trade offer pool indices that are always available. - * @param poolIndex the pool index + *

See the constants for vanilla trade offer pool IDs that are always available. + * @param pool the pool ID * @param factories the trade offer factories * @return this builder, for chaining - * @throws IndexOutOfBoundsException if {@code poolIndex} is out of bounds + * @throws IndexOutOfBoundsException if {@code pool} is out of bounds */ - default WanderingTraderOffersBuilder addOffersToPool(int poolIndex, Collection factories) { - return addOffersToPool(poolIndex, factories.toArray(TradeOffers.Factory[]::new)); + default WanderingTraderOffersBuilder addOffersToPool(Identifier pool, Collection factories) { + return addOffersToPool(pool, factories.toArray(TradeOffers.Factory[]::new)); } + + /** + * Returns all registered pool IDs, including vanilla ones. + * @return an unmodifiable set containing all registered pool IDs + */ + Set getPoolIds(); } } diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/TradeOfferInternals.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/TradeOfferInternals.java index e0801f426a..a167b8be23 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/TradeOfferInternals.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/impl/object/builder/TradeOfferInternals.java @@ -17,19 +17,25 @@ package net.fabricmc.fabric.impl.object.builder; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; import net.minecraft.village.TradeOffers; import net.minecraft.village.VillagerProfession; @@ -88,6 +94,12 @@ public static void printRefreshOffersWarning() { } public static class WanderingTraderOffersBuilderImpl implements TradeOfferHelper.WanderingTraderOffersBuilder { + static final Object2IntMap ID_TO_INDEX = Util.make(new Object2IntOpenHashMap<>(), idToIndex -> { + idToIndex.put(BUY_ITEMS_POOL, 0); + idToIndex.put(SELL_SPECIAL_ITEMS_POOL, 1); + idToIndex.put(SELL_COMMON_ITEMS_POOL, 2); + }); + /** * Make the trade list modifiable. */ @@ -98,24 +110,35 @@ static void initWanderingTraderTrades() { } @Override - public TradeOfferHelper.WanderingTraderOffersBuilder pool(int count, TradeOffers.Factory... factories) { + public TradeOfferHelper.WanderingTraderOffersBuilder pool(Identifier id, int count, TradeOffers.Factory... factories) { if (factories.length == 0) throw new IllegalArgumentException("cannot add empty pool"); if (count <= 0) throw new IllegalArgumentException("count must be positive"); + Objects.requireNonNull(id, "id cannot be null"); Pair pool = Pair.of(factories, count); initWanderingTraderTrades(); + ID_TO_INDEX.put(id, TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.size()); TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.add(pool); return this; } @Override - public TradeOfferHelper.WanderingTraderOffersBuilder addOffersToPool(int poolIndex, TradeOffers.Factory... factories) { - Objects.checkIndex(poolIndex, TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.size()); + public TradeOfferHelper.WanderingTraderOffersBuilder addOffersToPool(Identifier pool, TradeOffers.Factory... factories) { + if (!ID_TO_INDEX.containsKey(pool)) { + throw new IllegalArgumentException("pool %s is not registered".formatted(pool)); + } + + int poolIndex = ID_TO_INDEX.getInt(pool); initWanderingTraderTrades(); - Pair pool = TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.get(poolIndex); - TradeOffers.Factory[] modified = ArrayUtils.addAll(pool.getLeft(), factories); - TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.set(poolIndex, Pair.of(modified, pool.getRight())); + Pair poolPair = TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.get(poolIndex); + TradeOffers.Factory[] modified = ArrayUtils.addAll(poolPair.getLeft(), factories); + TradeOffers.REBALANCED_WANDERING_TRADER_TRADES.set(poolIndex, Pair.of(modified, poolPair.getRight())); return this; } + + @Override + public Set getPoolIds() { + return Collections.unmodifiableSet(ID_TO_INDEX.keySet()); + } } } diff --git a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/VillagerTypeTest1.java b/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/VillagerTypeTest1.java index 06d68e3662..d66b3489a3 100644 --- a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/VillagerTypeTest1.java +++ b/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/VillagerTypeTest1.java @@ -30,6 +30,7 @@ import net.minecraft.item.Items; import net.minecraft.registry.Registries; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import net.minecraft.util.math.random.Random; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOffers; @@ -40,6 +41,9 @@ import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper; public class VillagerTypeTest1 implements ModInitializer { + private static final Identifier FOOD_POOL_ID = ObjectBuilderTestConstants.id("food"); + private static final Identifier THING_POOL_ID = ObjectBuilderTestConstants.id("thing"); + @Override public void onInitialize() { TradeOfferHelper.registerVillagerOffers(VillagerProfession.ARMORER, 1, (factories, rebalanced) -> { @@ -58,12 +62,14 @@ public void onInitialize() { TradeOfferHelper.registerRebalancedWanderingTraderOffers(builder -> { builder.pool( - 1, + FOOD_POOL_ID, + 5, Registries.ITEM.stream().filter(item -> item.getFoodComponent() != null).map( item -> new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.NETHERITE_INGOT), new ItemStack(item), 3, 4, 0.15F)) ).toList() ); builder.addAll( + THING_POOL_ID, new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.NETHERITE_INGOT), new ItemStack(Items.MOJANG_BANNER_PATTERN), 1, 4, 0.15F)) ); builder.addOffersToPool( @@ -78,6 +84,10 @@ public void onInitialize() { new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.DIAMOND, 16), new ItemStack(Items.ELYTRA, 1), 1, 4, 0.15F)), new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.EMERALD, 3), new ItemStack(Items.LEAD, 2), 3, 4, 0.15F)) ); + builder.addOffersToPool( + FOOD_POOL_ID, + new SimpleTradeFactory(new TradeOffer(new ItemStack(Items.NETHERITE_INGOT), new ItemStack(Items.EGG), 3, 4, 0.15F)) + ); }); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {