【开发向】如何使用 Curios API 添加饰品与自定义饰品栏(以 Forge 为例)

您所在的位置:网站首页 我的世界如何添加物品 【开发向】如何使用 Curios API 添加饰品与自定义饰品栏(以 Forge 为例)

【开发向】如何使用 Curios API 添加饰品与自定义饰品栏(以 Forge 为例)

2024-07-09 22:15| 来源: 网络整理| 查看: 265

本篇教程由作者设定使用 CC BY-NC-SA 协议。

零、写在前面

协议与声明

注意,本文内容使用 CC: BY-NC-SA 协议,个人实现的代码以相同协议开源。而其它有说明的代码以 Forge 反编译的 Minecraft 源代码为主,所有函数、类和成员变量的命名使用了官方的反混淆表,函数参数和局域变量根据笔者的个人理解命名,仅作学习交流使用。

Curios API 的代码以 LGPL 协议开源,因而联动或依赖 Curios API 时,你可以用任何协议来开源你的 mod。

Curios API 有官方的英文文档,但内容非常少,本文在完整介绍 Wiki 的同时,加入了个人理解,并作出了适当补充。你也可以查看Curios API 的 Wiki 原文。

此教程以默认读者具备 Java 代码能力和 mod 开发的基础了解。

配置 build.gradle

首先你要在这里找到合适的 Curios API 版本,如1.18.2 的 Curios API v5.0.9.0。

然后,你要在 build.gradle 中加入对应的 maven 下载 url:

repositories {    maven { // TOP        url "https://cursemaven.com"    }    maven { //mirror        name = "ModMaven"        url = "https://modmaven.dev"    }}

然后,在 dependencies 中加入 curios:

dependencies {    minecraft 'net.minecraftforge:forge:1.18.2-40.1.0'    compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:1.18.2-5.0.9.0")}

这里使用了 compileOnly,目标是联动 curios,你也可以使用 implementation 保证运行测试时 curios 被加载(但多数情况下 implementation 用于必要前置模组)。

执行构建,gradle 会帮你下载好 curios 供你调用其 API。

配置 mods.toml(联动则无需)

当你希望 curios 作为你的必要或可选前置时,你需要配置 mods.toml。

比如你可以这样写:

[[dependencies.your_modid]]    modId="curios"    mandatory=false    versionRange="[5.0.9.0, 5.1.0.0)"    ordering="NONE"    side="BOTH"

此时 curios 成为了你的 mod 的可选前置。如果不安装,fml 不会报错,如果你的代码也是正确的,则相当于独立运行了你的 mod,没有任何联动内容被加载;如果安装了,但版本低于 5.0.9.0 或高于 5.1.0.0,则 fml 报错;如果安装且版本正确,则模组正常加载并与 Curios API 联动执行部分功能。

一、饰品栏相关

1.20 以前

1. 饰品栏的申请

Curios API 默认玩家没有任何饰品栏,如果你的 mod 注册了饰品,你需要将对应栏位申请下来,才能正常使用,否则玩家无法装备饰品。

饰品栏的申请方法很简单,需要订阅 InterModEnqueueEvent 事件,通过 InterModComms.sendTo 方法与 Curios API mod 交流。如:

private void enqueueIMC(final InterModEnqueueEvent event) {    if(ModList.get().isLoaded("curios")) {        InterModComms.sendTo("curios", SlotTypeMessage.REGISTER_TYPE, () -> SlotTypePreset.HEAD.getMessageBuilder().build());        InterModComms.sendTo("curios", SlotTypeMessage.REGISTER_TYPE, () -> SlotTypePreset.NECKLACE.getMessageBuilder().build());    }}

上述代码向 curios 申请了 Head(头部)与 Necklace(项链)两个饰品栏位。进入游戏后,在物品栏中点击 curios 图标,你将可以看到这两个栏位。

个人建议用到什么申请什么,尽量申请 SlotTypePreset 中有的栏位,如果真的没有合适的再自己创造。

2. 饰品栏的创造

如果你的 mod 单独创造了一个饰品栏,这意味着只有它和其附属 mod 与代码层面联动的 mod 可以使用。所以这个操作可能会引起兼容性问题,请尽量避免。

创造新的栏位,只需要在上述代码的基础上,将 SlotTypePreset.XXX.getMessageBuilder().build() 去掉,换成额外 build 出的 Message 即可:

private void enqueueIMC(final InterModEnqueueEvent event) {    if(ModList.get().isLoaded("curios")) {        InterModComms.sendTo("curios", SlotTypeMessage.REGISTER_TYPE, () -> new SlotTypeMessage.Builder("earings").ico(new ResourceLocation(MODID, "slot/earings")).priority(240).size(1).build());    }}

此时注册了一个名为耳环的栏位,且图标位于 assets//textures/slot/earings.png。

3. 饰品栏的修改

Curios API 还提供了一种通过 IMC 方法实现对应功能的操作,即修改一个饰品栏。当你希望修改其它 mod 定义的栏位或预设栏位时,你可以使用它。此时第三个参数的格式和前文相似,但你可以使用 Builder::size(int) 方法增加栏位可放置饰品的数量、Builder::lock()(相当于.size(0))方法锁定栏位、Builder::hide() 方法暂时隐藏栏位,以及 Builder::cosmetic() 方法为栏位添加装饰栏位。

用法与上述两个操作相同,在此不再赘述和举例。

1.20 及以后

Curios API 计划于 1.22 及之后的版本取消上述繁琐的利用 IMC 实现注册添加和修改的步骤,而是改用数据包的形式进行操作,使得整合包开发者、服务器管理员们无需了解模组开发即可自由操作 Curios API 的饰品栏成为了可能。当然,后文也介绍了如何允许任意物品被放入饰品栏中,同样是使用了数据包形式操作。

首先,在 data//curios/slots 中新建 xxx.json,其中 xxx 为你的栏位名字,为你的模组/数据包的 id。

接着,修改 xxx.json。size 字段用于前文的饰品栏注册;若 xxx 并非任意预设的栏位名,则相当于你创建了一个新的饰品栏位——当然,其它模组如果创建了相同名字的栏位,则也可以使用它;而修改 schema 中的字段的值则可以实现前文的饰品栏修改(如果字段 replace 设为 true,则将替换掉其它数据包或预设的栏位设置,而非与其它设置相合并;否则,会按一定规则合并)。下表展示了这个 json 文件的 schema 与合并规则,请按照要求修改:

字段字段类型默认值描述合并规则可空性replacebooleanfalse如果设为 true,则替换掉更低优先级的数据包中同一个栏位的设置-是sizeint1饰品栏位的默认数量,与使用 IMC 注册栏位等价最大值operationenum["SET, "ADD", "REMOVE"]SET与 size 配合使用。SET:设置栏位数量(最大值合并);ADD:增加栏位数量(求和合并);REMOVE:减少栏位数量(求和合并)-orderint1000栏位在物品栏中的顺序值,越低的顺序值使该栏位排名靠前,越高则排名靠后。最小值iconStringcurios:slot/empty_curios_slot饰品栏图标的资源 ID替换add_cosmeticbooleanfalse槽位是否包含一个仅提供渲染不提供功能的美化槽或use_native_guibooleantrue槽位是否在 Curios 的 GUI 中显示与render_togglebooleantrue槽位的物品是否渲染(渲染部分参见本文第三章)与

二、物品相关

首先,任何物品都可以被注册为饰品,不过具体细节需要说明。

1. 物品类

该物品必须实现 ICurioItem 接口,这一接口没有提供任何抽象函数,无须重写。但倘若你需要具体实现物品佩戴在身上后玩家可以获得的增益,则需要重写一些函数。如 onEquip 函数代表玩家将饰品佩戴在饰品栏上时触发的事件、onUnequip 函数则代表玩家将饰品从饰品栏中摘下时触发的事件、curioTick 则是玩家佩戴饰品时每一个 tick 执行的操作等,此外还支持装备声音、装备前的判断等等函数的重写。

下面的代码则实现了一个对装备饰品的实体在每个tick上执行固定逻辑的操作的抽象类:

public abstract class CuriosItem extends Item implements ICurioItem {   public CuriosItem(Properties properties) {      super(properties);   }   @Override   public void curioTick(SlotContext slotContext, ItemStack stack) {      this.equipmentTick(slotContext.entity());   }   protected abstract void equipmentTick(LivingEntity livingEntity);}

实现 equipmentTick 函数则可以完成其功能,如:

@Overrideprotected void equipmentTick(LivingEntity livingEntity) {   livingEntity.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 20, 0, false, false, true));}

上述代码为佩戴该饰品的生物提供了永久夜视的效果。

当然你甚至可以通过 livingEntity.level.getEntities().forEach 实现杀戮光环。

2. 栏位绑定

利用前文方法申请栏位后,不管栏位是预设的还是你自己创造的,都可以通过下述方式极快地实现饰品的栏位绑定。

你需要在 data/curios/tags/items 中对你申请的每个栏位分别创建一个 xxx.json 的 tag。其中 xxx 为你的栏位名字,预设的名字即是大写枚举名对应的小写,如 necklace.json 中定义了下述两种项链:

{  "values": [    "metal_necklaces:uranium_necklace",    "metal_necklaces:thorium_necklace"  ]}

分别是铀项链和钍项链。身在辐中不知辐

这样,对应 id 的 item 可通过 tag 被绑定在对应的栏位。

3. 如果 Curios 是你的联动或可选前置

由于 Forge 注册 Item 时用了 Supplier,所以你可以动态判断 curios 是否被加载,根据加载与否实例化不同的 Item 类,如:

public static final RegistryObject PEARL_BRACELET = REGISTER.register(      "pearl_bracelet", () -> makeCuriosItem(            ITEM_GROUP, livingEntity -> livingEntity.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, 10, 0, false, false, true))      ));public static Item makeCuriosItem(CreativeModeTab tab, Consumer effect) {   if(modList.isLoaded("curios")) {      return CuriosItemFactory.make(new Item.Properties().stacksTo(1).tab(tab), effect);   }   return new Item(new Item.Properties().stacksTo(1).tab(tab));}

而 CuriosItemFactory 类则可以这样写:

public final class CuriosItemFactory {   public static Item make(Item.Properties props, Consumer effect) {      return new CuriosItem(props) {            @Override            protected void equipmentTick(LivingEntity livingEntity) {               effect.accept(livingEntity);            }      };   }}

通过这两层封装,结合 Java 的类懒惰加载方式,则可以实现注册同一个 Item 时根据 curios 加载与否采取不同的逻辑。

如果不这样做,curios 没有运行时,你的 mod 也会报错,并提示你无法找到类。所以这也是 Forge 模组开发的常用联动技巧。

三、渲染

非常反直觉的是,Curios API 并没有为用户简化代码而提供饰品渲染的接口。不过 Curios 提供了装备饰品后调用渲染器的注册接口,即 CuriosRendererRegistry::register。因此你可以在订阅 FMLClientSetupEvent 的函数中通过如下方式注册渲染器:

@SubscribeEventpublic static void onClientSetup(FMLClientSetupEvent event) {   event.enqueueWork(() -> {      if(ModList.get().isLoaded("curios")) {         CuriosRenderers.registerRenderers();      }   });}

CuriosRenderers 类中:

public static void registerRenderers() {   CuriosRendererRegistry.register(MNItems.URANIUM_NECKLACE.get(), NecklaceRenderer::new);   CuriosRendererRegistry.register(MNItems.THORIUM_NECKLACE.get(), NecklaceRenderer::new);}

而 NecklaceRenderer 可以通过类似于盔甲的方式渲染项链:

public class NecklaceRenderer implements ICurioRenderer {   public static final ModelLayerLocation NECKLACE = new ModelLayerLocation(new ResourceLocation(MODID, "necklace"), "main");      private final HumanoidModel model;   public NecklaceRenderer() {      this.model = new HumanoidModel(Minecraft.getInstance().getEntityModels().bakeLayer(NECKLACE));   }   @Override   public  void render(ItemStack stack, SlotContext slotContext, PoseStack matrixStack, RenderLayerParent renderLayerParent,                                                        MultiBufferSource renderTypeBuffer, int light, float limbSwing, float limbSwingAmount,                                                        float partialTicks, float ageInTicks, float netHeadYaw, float headPitch) {      if(stack.getItem() instanceof CuriosItem curiosItem) {         ICurioRenderer.followBodyRotations(slotContext.entity(), this.model);         VertexConsumer vertexConsumer = renderTypeBuffer.getBuffer(RenderType.entityCutout(new ResourceLocation(               MODID, "textures/model/curios/" + ForgeRegistries.ITEMS.getKey(curiosItem).getPath() + ".png"         )));         this.model.renderToBuffer(matrixStack, vertexConsumer, light, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F);      }   }}

那么你可以在assets//textures/model/curios/xxx.png里放置项链的模型贴图,其中xxx是物品名,而贴图对应的模型必须是像Zombie和Player那样的模型。需要注意的是,NECKLACE和其它ModelLayerLocation一样,需要在订阅EntityRenderersEvent.RegisterLayerDefinitions事件的函数中通过event.registerLayerDefinition方法注册:

event.registerLayerDefinition(NecklaceRenderer.NECKLACE, () -> LayerDefinition.create(HumanoidModel.createMesh(new CubeDeformation(0.25F), 0.0F), 64, 64));

四、总结

以上则是笔者关于 Curios API 模组用法和教程的拙见,如有不完美或不详细之处,敬请指出。如有其他需要更新的内容,笔者也会尽快修改!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3