工具与盔甲
工具是主要用于破坏方块的物品。许多 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。