本篇教程的视频

本篇教程的源代码

Github地址:TutorialMod-Trade-1.21

介绍

在我们的游戏中,也许我们需要一些特殊的物品,但这些物品获取起来可能没有那么容易,这时候我们可以通过交易的方式来获得这些物品

本篇教程采用FabricAPI来实现与村民的自定义交易,因为原版的交易是硬编码的,我们如果要修改必须得使用Mixin。相比较而言,使用FabricAPI来实现自定义交易会更加方便

查看源代码

我们来查看TradeOffers这个类,这个类是定义了原版的交易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static final Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>> PROFESSION_TO_LEVELED_TRADE = Util.make(
Maps.<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>>newHashMap(),
map -> {
map.put(
VillagerProfession.FARMER,
copyToFastUtilMap(
ImmutableMap.of(
1,
new TradeOffers.Factory[]{
new TradeOffers.BuyItemFactory(Items.WHEAT, 20, 16, 2),
new TradeOffers.BuyItemFactory(Items.POTATO, 26, 16, 2),
new TradeOffers.BuyItemFactory(Items.CARROT, 22, 16, 2),
new TradeOffers.BuyItemFactory(Items.BEETROOT, 15, 16, 2),
new TradeOffers.SellItemFactory(Items.BREAD, 1, 6, 16, 1)
},
2,
new TradeOffers.Factory[]{
new TradeOffers.BuyItemFactory(Blocks.PUMPKIN, 6, 12, 10),
new TradeOffers.SellItemFactory(Items.PUMPKIN_PIE, 1, 4, 5),
new TradeOffers.SellItemFactory(Items.APPLE, 1, 4, 16, 5)
},
3,
new TradeOffers.Factory[]{new TradeOffers.SellItemFactory(Items.COOKIE, 3, 18, 10), new TradeOffers.BuyItemFactory(Blocks.MELON, 4, 12, 20)},
4,
new TradeOffers.Factory[]{
new TradeOffers.SellItemFactory(Blocks.CAKE, 1, 1, 12, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.NIGHT_VISION, 100, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.JUMP_BOOST, 160, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.WEAKNESS, 140, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.BLINDNESS, 120, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.POISON, 280, 15),
new TradeOffers.SellSuspiciousStewFactory(StatusEffects.SATURATION, 7, 15)
},
5,
new TradeOffers.Factory[]{
new TradeOffers.SellItemFactory(Items.GOLDEN_CARROT, 3, 3, 30), new TradeOffers.SellItemFactory(Items.GLISTERING_MELON_SLICE, 4, 3, 30)
}
)
)
);
...

这里我们看其中的一部分,它是直接定义了Map<VillagerProfession, Int2ObjectMap<TradeOffers.Factory[]>>这个类型的变量,这个Map存储的是村民职业对应的交易内容

我们可以看到,这里的交易内容是直接硬编码的,我们如果要修改这个Map,肯定是得使用Mixin

观察XXXFactory方法

上面我们看到了各种各样的交易内容,这些交易内容都是通过XXXFactory方法来创建的,我们来看看这些方法

1
2
TradeOffers.BuyItemFactory(Items.WHEAT, 20, 16, 2)
TradeOffers.BuyItemFactory(Items.WATER_BUCKET, 1, 1, 1, 2)

这个是玩家向村民出售物品的交易,即玩家出售物品,村民给玩家绿宝石

这里的参数分别是物品交易数量最大交易次数村民获得经验值价格

其中价格是绿宝石的数量,可以缺省,缺省的话就是默认的1

1
2
TradeOffers.SellItemFactory(Items.BREAD, 1, 6, 16, 1)
TradeOffers.SellItemFactory(new ItemStack(Items.IRON_AXE), 3, 1, 12, 1, 0.2F)

这个是玩家购买物品的交易,即玩家购买物品,村民给玩家物品

这里的参数分别是物品交易数量价格最大交易次数村民获得经验值打折乘数

其中打折乘数是可以缺省的,缺省的话就是默认的0.05F

1
TradeOffers.SellSuspiciousStewFactory(StatusEffects.NIGHT_VISION, 100, 15)

这个是玩家购买谜之炖菜的交易

这里的参数分别是效果效果的持续时间村民获得的经验

1
TradeOffers.ProcessItemFactory(Blocks.GRAVEL, 10, 1, Items.FLINT, 10, 12, 1, 0.05F)

这个也是是玩家购买物品的交易,不过在购买的时候要提供绿宝石和特定的物品,村民给玩家物品

这里的参数分别是玩家给出的物品物品数量价格村民给出的物品物品数量最大交易次数村民获得经验值打折乘数

其实很多交易的参数都是类似的,这里我们只是列举了一些常见的交易,里面还有很多其他的交易,具体的可以查看源代码

创建交易

那么我们就来开始自定义自己的交易吧

创建ModCustomTrades类

1
2
3
public class ModCustomTrades {

}

首先我们创建一个ModCustomTrades类,这个类用来存放我们自定义的交易

1
2
3
4
5
6
public static void registerModCustomTrades() {
TradeOfferHelper.registerVillagerOffers(VillagerProfession.FARMER, 1, factories -> {
factories.add(new TradeOffers.BuyItemFactory(ModItems.CORN, 5, 12, 5, 2));
factories.add(new TradeOffers.SellItemFactory(ModItems.CORN_SEEDS, 1, 12, 5, 2, 0.5f));
});
}

我们创建一个registerModCustomTrades方法,这个是用于之后初始化的

在这个方法中,我们使用TradeOfferHelper.registerVillagerOffers来注册我们的交易

这个是FabricAPI提供的,我们可以直接使用

里面的参数是职业村民等级交易内容

在此之后我们用factories.add新增交易内容,这里我们新增了两个交易,一个是玩家出售玉米,一个是玩家购买玉米种子

我们可以直接使用原版的方法来创建交易

1
2
3
4
TradeOfferHelper.registerVillagerOffers(VillagerProfession.FARMER, 2, factories -> {
factories.add(new TradeOffers.BuyItemFactory(ModItems.STRAWBERRY, 5, 12, 5, 2));
factories.add(new TradeOffers.ProcessItemFactory(Items.MILK_BUCKET, 1, 2, ModItems.CHEESE, 3, 16, 1, 0.5f));
});

我们再新增一个交易,这个交易是玩家购买草莓奶酪,其中购买奶酪的时候需要提供牛奶桶(只是这个会有一个bug,桶也会被消耗)

另外在1.20的教程中,我们还编写了图书管理员的新增交易,写了个特定附魔书,但在1.21中,变得没那么方便了

因为在1.21中,附魔大改,变成数据驱动的了,所有的附魔的注册名和等级得从注册表中获取

1
2
3
4
5
6
7
TradeOfferHelper.registerVillagerOffers(VillagerProfession.LIBRARIAN,1,
factories -> {
factories.add((entity, random) -> new TradeOffer(
new ItemStack(ModItems.ICE_ETHER,16),
EnchantedBookItem.forEnchantment(new EnchantmentLevelEntry(Enchantments.SHARPNESS,2)),
3,12,0.05f));
});

这是1.20中图书管理员的交易的内容

但在1.21中,EnchantmentLevelEntry的第一个参数要求的是RegistryEntry<Enchantment>,而不是Enchantment,我们得先获取注册表才能来写这个东西

初始化交易方法

1
ModCustomTrades.registerModCustomTrades();

到模组主类中,我们在onInitialize方法中调用ModCustomTrades.registerModCustomTrades()来初始化我们的交易

在此之后我们就可以进入我们的游戏测试了,可以找个村庄也可以自己生成一个村民来测试我们的交易