工具与盔甲

工具与盔甲

工具是主要用于破坏方块的物品。许多 Mod 会添加新的工具套装(例如铜工具)或工具类型(例如锤子)。

自定义工具套装

一套工具通常包含镐、斧、锹、锄和剑五种物品。剑并非传统意义上的工具,但为了统一也一并介绍。它们分别对应 PickaxeItem、AxeItem、ShovelItem、HoeItem 和 SwordItem。类继承关系如下:

Item
- TieredItem
    - DiggerItem
        - AxeItem
        - HoeItem
        - PickaxeItem
        - ShovelItem
    - SwordItem

TieredItem 为具有特定 Tier 的物品提供辅助功能;DiggerItem 为用于破坏方块的物品提供辅助功能。剪刀等通常也视为工具的物品并不在此继承体系中,它们直接继承 Item,自行实现破坏逻辑。

创建标准工具套装前,先定义 Tier。参考值可查看原版 Tiers 枚举。下面使用铜工具示例,可换成自己的材料并调整数值。

// We place copper somewhere between stone and iron.
public static final Tier COPPER_TIER = new SimpleTier(
        // The tag that determines what blocks this tool cannot break. See below for more information.
        MyBlockTags.INCORRECT_FOR_COPPER_TOOL,
        // Determines the durability of the tier.
        // Stone is 131, iron is 250.
        200,
        // Determines the mining speed of the tier. Unused by swords.
        // Stone uses 4, iron uses 6.
        5f,
        // Determines the attack damage bonus. Different tools use this differently. For example, swords do (getAttackDamageBonus() + 4) damage.
        // Stone uses 1, iron uses 2, corresponding to 5 and 6 attack damage for swords, respectively; our sword does 5.5 damage now.
        1.5f,
        // Determines the enchantability of the tier. This represents how good the enchantments on this tool will be.
        // Gold uses 22, we put copper slightly below that.
        20,
        // Determines the repair ingredient of the tier. Use a supplier for lazy initializing.
        () -> Ingredient.of(Tags.Items.INGOTS_COPPER)
);

定义 Tier 后就可以注册工具。所有工具构造函数都接收相同的四个参数:

//ITEMS is a DeferredRegister<Item>
public static final Supplier<SwordItem> COPPER_SWORD = ITEMS.register("copper_sword", () -> new SwordItem(
        // The tier to use.
        COPPER_TIER,
        // The item properties. We don't need to set the durability here because TieredItem handles that for us.
        new Item.Properties().attributes(
            // There are `createAttributes` methods in either the class or subclass of each DiggerItem
            SwordItem.createAttributes(
                // The tier to use.
                COPPER_TIER,
                // The type-specific attack damage bonus. 3 for swords, 1.5 for shovels, 1 for pickaxes, varying for axes and hoes.
                3,
                // The type-specific attack speed modifier. The player has a default attack speed of 4, so to get to the desired
                // value of 1.6f, we use -2.4f. -2.4f for swords, -3f for shovels, -2.8f for pickaxes, varying for axes and hoes.
                -2.4f,
            )
        )
));
public static final Supplier<AxeItem> COPPER_AXE = ITEMS.register("copper_axe", () -> new AxeItem(...));
public static final Supplier<PickaxeItem> COPPER_PICKAXE = ITEMS.register("copper_pickaxe", () -> new PickaxeItem(...));
public static final Supplier<ShovelItem> COPPER_SHOVEL = ITEMS.register("copper_shovel", () -> new ShovelItem(...));
public static final Supplier<HoeItem> COPPER_HOE = ITEMS.register("copper_hoe", () -> new HoeItem(...));

标签

创建 Tier 时,会指定一个方块标签,包含用此工具破坏后不会掉落物品的方块。例如,minecraft:incorrect_for_stone_tool 包含钻石矿石等方块,minecraft:incorrect_for_iron_tool 包含黑曜石和远古残骸等方块。

为了方便按挖掘等级分配方块,还有表示“需要此等级工具”的标签。例如,minecraft:needs_iron_tool 包含钻石矿石等方块,minecraft:needs_diamond_tool 包含黑曜石和远古残骸等方块。

可以复用现有 incorrect 标签。例如,如果希望铜工具只是耐久更高的石工具,就传入 BlockTags#INCORRECT_FOR_STONE_TOOL。

也可以创建自己的标签:

// This tag will allow us to add these blocks to the incorrect tags that cannot mine them
public static final TagKey<Block> NEEDS_COPPER_TOOL = TagKey.create(BuiltInRegistries.BLOCK.key(), ResourceLocation.fromNamespaceAndPath(MOD_ID, "needs_copper_tool"));

// This tag will be passed into our tier
public static final TagKey<Block> INCORRECT_FOR_COPPER_TOOL = TagKey.create(BuiltInRegistries.BLOCK.key(), ResourceLocation.fromNamespaceAndPath(MOD_ID, "incorrect_for_cooper_tool"));

然后填充标签。例如,让铜工具可以挖掘金矿石、金块和红石矿石,但不能挖掘钻石或绿宝石矿石。(红石块本来就能用石工具挖掘。)标签文件位于 src/main/resources/data/mod_id/tags/block/needs_copper_tool.json,其中 mod_id 为 Mod ID:

{
    "values": [
        "minecraft:gold_block",
        "minecraft:raw_gold_block",
        "minecraft:gold_ore",
        "minecraft:deepslate_gold_ore",
        "minecraft:redstone_ore",
        "minecraft:deepslate_redstone_ore"
    ]
}

接着为传入等级的标签设置排除约束:排除铜工具标签中的方块,同时保留石工具无法正确挖掘的其他方块。原文给出的路径为 src/main/resources/data/mod_id/tags/block/incorrect_for_cooper_tool.json:

{
    "values": [
        "#minecraft:incorrect_for_stone_tool"
    ],
    "remove": [
        "#mod_id:needs_copper_tool"
    ]
}

最后,如上文所示,将标签传入等级创建过程。

要检查工具能否让某个方块状态产生掉落物,调用 Tool#isCorrectForDrops。通过向 ItemStack#get 传入 DataComponents#TOOL,可以取得 Tool。

自定义工具

通过 Item.Properties#component,在物品默认组件列表中添加 Tool 数据组件(DataComponents#TOOL),即可创建自定义工具。DiggerItem 是一种实现,它接收上文的 Tier 来构造 Tool,并提供 #createAttributes 便捷方法,用于向 Item.Properties#attributes 提供攻击伤害、攻击速度等属性。

Tool 包含 Tool.Rule 列表、持有工具时的默认挖掘速度(默认 1),以及挖掘方块时工具承受的损伤值(默认 1)。每条规则包含三项信息:适用方块的 HolderSet、可选的集合内方块挖掘速度,以及可选的布尔值,决定工具能否让这些方块掉落物品。如果可选值未设置,则继续检查其他规则。如果所有规则都未匹配,就使用默认挖掘速度,且不能产生方块掉落物。

提示 通过 Registry#getOrCreateTag,可以由 TagKey 创建 HolderSet。

多功能工具(例如将斧和镐结合在一个物品中)或其他工具类物品,不必继承现有 TieredItem。可组合以下部分实现:

  • 通过 Item.Properties#component 设置 DataComponents#TOOL,添加具有自定义规则的 Tool。
  • 通过 Item.Properties#attributes 添加攻击伤害、攻击速度等属性。
  • 重写 IItemExtension#canPerformAction,决定可执行哪些 ItemAbility。
  • 如果右键时要根据 ItemAbility 修改方块状态,调用 IBlockExtension#getToolModifiedState。
  • 将工具加入适当的 minecraft:enchantable/* 标签,使其能够接受特定附魔。

ItemAbility

ItemAbility 抽象表示物品能做和不能做的操作,包括左键和右键行为。NeoForge 在 ItemAbilities 中提供默认能力:

  • 挖掘能力:涵盖前述四种 DiggerItem,以及剑和剪刀的挖掘。
  • 斧的右键能力:去皮、刮除铜氧化层、去除铜块蜡层。
  • 剪刀能力:采集蜜脾、雕刻南瓜、解除绊线。
  • 锹铲出土径、剑横扫、锄耕地、盾牌格挡和钓鱼竿抛竿等能力。

调用 ItemAbility#get 创建自定义能力,必要时它会新建 ItemAbility。然后在自定义工具类型中按需重写 IItemExtension#canPerformAction。

调用 IItemStackExtension#canPerformAction 可查询物品堆能否执行某个能力。它适用于任意 Item,并不限于工具。

盔甲

与工具类似,盔甲也使用等级系统,但体系不同:工具称为 Tier,盔甲则称为 ArmorMaterial。下面仍以铜盔甲为例,可按需调整。与 Tier 不同,ArmorMaterial 必须注册。原版数值可查看 ArmorMaterials。

// ARMOR_MATERIALS is a DeferredRegister<ArmorMaterial>

// We place copper somewhere between chainmail and iron.
public static final Holder<ArmorMaterial> COPPER_ARMOR_MATERIAL =
    ARMOR_MATERIALS.register("copper", () -> new ArmorMaterial(
        // Determines the defense value of this armor material, depending on what armor piece it is.
        Util.make(new EnumMap<>(ArmorItem.Type.class), map -> {
            map.put(ArmorItem.Type.BOOTS, 2);
            map.put(ArmorItem.Type.LEGGINGS, 4);
            map.put(ArmorItem.Type.CHESTPLATE, 6);
            map.put(ArmorItem.Type.HELMET, 2);
            map.put(ArmorItem.Type.BODY, 4);
        }),
        // Determines the enchantability of the tier. This represents how good the enchantments on this armor will be.
        // Gold uses 25, we put copper slightly below that.
        20,
        // Determines the sound played when equipping this armor.
        // This is wrapped with a Holder.
        SoundEvents.ARMOR_EQUIP_GENERIC,
        // Determines the repair item for this armor.
        () -> Ingredient.of(Tags.Items.INGOTS_COPPER),
        // Determines the texture locations of the armor to apply when rendering
        // This can also be specified by overriding 'IItemExtension#getArmorTexture' on your item if the armor texture needs to be more dynamic
        List.of(
            // Creates a new armor texture that will be located at:
            // - 'assets/mod_id/textures/models/armor/copper_layer_1.png' for the outer texture
            // - 'assets/mod_id/textures/models/armor/copper_layer_2.png' for the inner texture (only legs)
            new ArmorMaterial.Layer(
                ResourceLocation.fromNamespaceAndPath(MOD_ID, "copper")
            ),
            // Creates a new armor texture that will be rendered on top of the previous at:
            // - 'assets/mod_id/textures/models/armor/copper_layer_1_overlay.png' for the outer texture
            // - 'assets/mod_id/textures/models/armor/copper_layer_2_overlay.png' for the inner texture (only legs)
            // 'true' means that the armor material is dyeable; however, the item must also be added to the 'minecraft:dyeable' tag
            new ArmorMaterial.Layer(
                ResourceLocation.fromNamespaceAndPath(MOD_ID, "copper"), "_overlay", true
            )
        ),
        // Returns the toughness value of the armor. The toughness value is an additional value included in
        // damage calculation, for more information, refer to the Minecraft Wiki's article on armor mechanics:
        // https://minecraft.wiki/w/Armor#Armor_toughness
        // Only diamond and netherite have values greater than 0 here, so we just return 0.
        0,
        // Returns the knockback resistance value of the armor. While wearing this armor, the player is
        // immune to knockback to some degree. If the player has a total knockback resistance value of 1 or greater
        // from all armor pieces combined, they will not take any knockback at all.
        // Only netherite has values greater than 0 here, so we just return 0.
        0
    ));

然后在物品注册中使用该盔甲材料:

//ITEMS is a DeferredRegister<Item>
public static final Supplier<ArmorItem> COPPER_HELMET = ITEMS.register("copper_helmet", () -> new ArmorItem(
        // The armor material to use.
        COPPER_ARMOR_MATERIAL,
        // The armor type to use.
        ArmorItem.Type.HELMET,
        // The item properties where we set the durability.
        // ArmorItem.Type is an enum of five values: HELMET, CHESTPLATE, LEGGINGS, BOOTS, and BODY.
        // BODY is used for non-player entities like wolves or horses.
        // Vanilla armor materials determine this by using a base value and multiplying it with a type-specific constant.
        // The constants are 13 for BOOTS, 15 for LEGGINGS, 16 for CHESTPLATE, 11 for HELMET, and 16 for BODY.
        // If we don't want to use these ratios, we can set the durability normally.
        new Item.Properties().durability(ArmorItem.Type.HELMET.getDurability(15))
));
public static final Supplier<ArmorItem> COPPER_CHESTPLATE = ITEMS.register("copper_chestplate", () -> new ArmorItem(...));
public static final Supplier<ArmorItem> COPPER_LEGGINGS = ITEMS.register("copper_leggings", () -> new ArmorItem(...));
public static final Supplier<ArmorItem> COPPER_BOOTS = ITEMS.register("copper_boots", () -> new ArmorItem(...));

制作盔甲纹理时,建议以原版盔甲纹理为基础,观察各区域对应的位置。


来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交 4a680cdd4d15。