伤害类型与伤害来源
伤害类型与伤害来源
伤害类型表示对实体施加的伤害类型——物理伤害、火焰伤害、溺水伤害、魔法伤害、虚空伤害等。伤害类型的区分用于各种免疫(例如烈焰人不会受到火焰伤害)、附魔(例如爆炸保护仅对爆炸伤害有效)以及更多用例。
伤害类型可以说是伤害来源的模板。换句话说,伤害来源可以被视为伤害类型的实例。伤害类型在代码中以 ResourceKey
的形式存在,但其所有属性都在数据包中定义。另一方面,伤害来源是根据数据包文件中的值在需要时由游戏创建的。它们可以保存额外的上下文,例如攻击实体。
创建伤害类型
首先,您需要创建自己的 DamageType
。DamageType
是一个数据包注册表,因此新的 DamageType
不是在代码中注册的,而是在添加相应文件时自动注册的。然而,我们仍然需要为代码提供一个获取伤害来源的入口。我们通过指定一个资源键来实现这一点:
public static final ResourceKey<DamageType> EXAMPLE_DAMAGE =
ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "example"));
现在我们可以从代码中引用它,让我们在数据文件中指定一些属性。我们的数据文件位于 data/examplemod/damage_type/example.json
(将 examplemod
和 example
替换为 Mod ID 和资源位置的名称),并包含以下内容:
{
// 伤害类型的死亡消息 ID。完整的死亡消息翻译键将是 "death.examplemod.example"(替换为相应的 Mod ID 和名称)。
"message_id": "example",
// 此伤害类型的伤害值是否随难度变化。有效的原版值为:
// - "never":伤害值在任何难度下保持不变。通常用于玩家造成的伤害类型。
// - "when_caused_by_living_non_player":如果伤害是由某种生物(包括间接造成的,例如骷髅射出的箭)但不是玩家造成的,则伤害值会缩放。
// - "always":伤害值总是缩放。通常用于类似爆炸的伤害。
"scaling": "when_caused_by_living_non_player",
// 受到此类伤害时消耗的饥饿值。
"exhaustion": 0.1,
// 受到此类伤害时应用的伤害效果(目前仅为音效)。可选。
// 有效的原版值为 "hurt"(默认)、"thorns"、"drowning"、"burning"、"poking" 和 "freezing"。
"effects": "hurt",
// 死亡消息类型。决定死亡消息的构建方式。可选。
// 有效的原版值为 "default"(默认)、"fall_variants" 和 "intentional_game_design"。
"death_message_type": "default"
}
提示:scaling
、effects
和 death_message_type
字段在内部分别由枚举 DamageScaling
、DamageEffects
和 DeathMessageType
控制。如果需要,可以扩展这些枚举以添加自定义值。
相同的格式也用于原版的伤害类型,如果需要,包开发者可以更改这些值。
创建和使用伤害来源
DamageSource
通常在调用 Entity#hurt
时动态创建。请注意,由于伤害类型是数据包注册表,您需要 RegistryAccess
来查询它们,可以通过 Level#registryAccess
获取。要创建 DamageSource
,请调用 DamageSource
构造函数,最多接受四个参数:
DamageSource damageSource = new DamageSource(
// 要使用的伤害类型持有者。从注册表中查询。这是唯一必需的参数。
registryAccess.registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(EXAMPLE_DAMAGE),
// 直接实体。例如,如果骷髅射中了你,骷髅将是造成伤害的实体(= 上面的参数),而箭将是直接实体(= 此参数)。与造成伤害的实体类似,这并不总是适用,因此可以为空。可选,默认为 null。
null,
// 造成伤害的实体。这并不总是适用(例如,当从世界掉落时),因此可以为空。可选,默认为 null。
null,
// 伤害来源的位置。这很少使用,一个例子是故意游戏设计(= 下界床爆炸)。可为空且可选,默认为 null。
null
);
警告:DamageSources#source
是 new DamageSource
的包装器,它交换了第二个和第三个参数(直接实体和造成伤害的实体)。请确保您将正确的值传递给正确的参数。
如果 DamageSource
没有任何实体或位置上下文,将其缓存在字段中是有意义的。对于具有实体或位置上下文的 DamageSource
,通常会添加辅助方法,如下所示:
public static DamageSource exampleDamage(Entity causer) {
return new DamageSource(
causer.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(EXAMPLE_DAMAGE),
causer);
}
提示:原版的 DamageSource
工厂可以在 DamageSources
中找到,原版的 DamageType
资源键可以在 DamageTypes
中找到。
伤害来源的首要用例是 Entity#hurt
。每当实体受到伤害时,都会调用此方法。要使用我们自己的伤害类型伤害实体,我们只需自己调用 Entity#hurt
:
// 第二个参数是伤害量,以半颗心为单位。
entity.hurt(exampleDamage(player), 10);
其他伤害类型特定的行为,例如无敌检查,通常通过伤害类型标签运行。这些标签由 Minecraft 和 NeoForge 添加,可以分别在 DamageTypeTags
和 Tags.DamageTypes
中找到。
数据生成
有关更多信息,请参见 数据包注册表的数据生成。
伤害类型 JSON 文件可以数据生成。由于伤害类型是数据包注册表,我们将 DatapackBuiltinEntriesProvider
添加到 GatherDataEvent
中,并将我们的伤害类型放入 RegistrySetBuilder
中:
// 在您的数据生成类中
@SubscribeEvent
public static void onGatherData(GatherDataEvent event) {
CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();
event.getGenerator().addProvider(
event.includeServer(),
output -> new DatapackBuiltinEntriesProvider(output, lookupProvider, new RegistrySetBuilder()
// 为伤害类型添加数据包内置条目提供者。如果此 lambda 变得更长,
// 为了可读性,应将其提取到单独的方法中。
.add(Registries.DAMAGE_TYPE, bootstrap -> {
// 使用 new DamageType() 创建伤害类型的代码表示。
// 参数按上述顺序映射到 JSON 文件的值。
// 除了消息 ID 和饥饿值之外的所有参数都是可选的。
bootstrap.register(EXAMPLE_DAMAGE, new DamageType(EXAMPLE_DAMAGE.location(),
DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER,
0.1f,
DamageEffects.HURT,
DeathMessageType.DEFAULT))
})
// 如果适用,添加其他数据包条目的数据包提供者。
.add(...),
Set.of(ExampleMod.MOD_ID)
)
);
}